about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.reuse/dep55
-rw-r--r--Cargo.lock102
-rw-r--r--LICENSES/BSD-2-Clause.txt9
-rw-r--r--compiler/rustc_ast/src/ast.rs40
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs12
-rw-r--r--compiler/rustc_ast/src/token.rs50
-rw-r--r--compiler/rustc_ast/src/util/literal.rs27
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs52
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs15
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs21
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs3
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs10
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/concat.rs25
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs83
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/bounds.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/debug.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/decodable.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/encodable.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs69
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/hash.rs1
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs9
-rw-r--r--compiler/rustc_codegen_gcc/src/consts.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/debuginfo.rs57
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs8
-rw-r--r--compiler/rustc_codegen_gcc/src/type_.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/utils.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs18
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/glue.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs11
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs491
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs71
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs28
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs173
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/statement.rs47
-rw-r--r--compiler/rustc_codegen_ssa/src/mono_item.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/builder.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/debuginfo.rs5
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs8
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs8
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs39
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs48
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs75
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs28
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/traits.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0322.md3
-rw-r--r--compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl4
-rw-r--r--compiler/rustc_error_messages/locales/en-US/parser.ftl12
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs3
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs3
-rw-r--r--compiler/rustc_errors/src/lib.rs4
-rw-r--r--compiler/rustc_expand/src/base.rs13
-rw-r--r--compiler/rustc_expand/src/build.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs48
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs76
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs17
-rw-r--r--compiler/rustc_feature/src/accepted.rs6
-rw-r--r--compiler/rustc_feature/src/active.rs4
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_graphviz/src/lib.rs6
-rw-r--r--compiler/rustc_hir/src/hir.rs29
-rw-r--r--compiler/rustc_hir/src/intravisit.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs98
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs66
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs70
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/collect/lifetimes.rs48
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs19
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs145
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs77
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs137
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs348
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs2
-rw-r--r--compiler/rustc_lexer/src/lib.rs8
-rw-r--r--compiler/rustc_lint/src/builtin.rs20
-rw-r--r--compiler/rustc_lint/src/hidden_unicode_codepoints.rs15
-rw-r--r--compiler/rustc_lint/src/late.rs2
-rw-r--r--compiler/rustc_lint/src/let_underscore.rs5
-rw-r--r--compiler/rustc_lint/src/levels.rs4
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs14
-rw-r--r--compiler/rustc_lint/src/types.rs43
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs8
-rw-r--r--compiler/rustc_llvm/build.rs2
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp6
-rw-r--r--compiler/rustc_metadata/src/fs.rs11
-rw-r--r--compiler/rustc_metadata/src/lib.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs20
-rw-r--r--compiler/rustc_middle/Cargo.toml2
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs64
-rw-r--r--compiler/rustc_middle/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/mir/generic_graphviz.rs7
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs837
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs530
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs321
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation/tests.rs19
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs36
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/pointer.rs25
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs4
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs67
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs40
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs2
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs20
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs4
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs14
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs4
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs11
-rw-r--r--compiler/rustc_middle/src/ty/impls_ty.rs13
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs78
-rw-r--r--compiler/rustc_middle/src/ty/vtable.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs16
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/graphviz.rs5
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/lattice.rs34
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs1
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs927
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs7
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs19
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs530
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs16
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs348
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs10
-rw-r--r--compiler/rustc_parse/src/errors.rs119
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs13
-rw-r--r--compiler/rustc_parse/src/lib.rs1
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs4
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs12
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs257
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs4
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs2
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs28
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs58
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs10
-rw-r--r--compiler/rustc_parse_format/src/lib.rs150
-rw-r--r--compiler/rustc_passes/src/check_attr.rs17
-rw-r--r--compiler/rustc_passes/src/dead.rs12
-rw-r--r--compiler/rustc_passes/src/lang_items.rs2
-rw-r--r--compiler/rustc_passes/src/liveness.rs5
-rw-r--r--compiler/rustc_passes/src/stability.rs30
-rw-r--r--compiler/rustc_passes/src/upvars.rs5
-rw-r--r--compiler/rustc_privacy/src/lib.rs71
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs4
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs2
-rw-r--r--compiler/rustc_resolve/src/lib.rs5
-rw-r--r--compiler/rustc_save_analysis/src/dump_visitor.rs12
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs2
-rw-r--r--compiler/rustc_save_analysis/src/sig.rs4
-rw-r--r--compiler/rustc_session/src/errors.rs162
-rw-r--r--compiler/rustc_span/src/symbol.rs54
-rw-r--r--compiler/rustc_target/src/abi/mod.rs10
-rw-r--r--compiler/rustc_target/src/spec/mod.rs2
-rw-r--r--compiler/rustc_target/src/spec/wasm32_wasi.rs6
-rw-r--r--compiler/rustc_target/src/spec/wasm_base.rs4
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs48
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs93
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs75
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs68
-rw-r--r--compiler/rustc_traits/Cargo.toml6
-rw-r--r--compiler/rustc_traits/src/chalk/db.rs3
-rw-r--r--compiler/rustc_traits/src/chalk/lowering.rs3
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs2
-rw-r--r--library/alloc/benches/str.rs65
-rw-r--r--library/alloc/src/alloc.rs12
-rw-r--r--library/alloc/src/boxed.rs2
-rw-r--r--library/alloc/src/collections/btree/map.rs14
-rw-r--r--library/alloc/src/collections/btree/set.rs10
-rw-r--r--library/alloc/src/rc.rs84
-rw-r--r--library/alloc/src/sync.rs85
-rw-r--r--library/alloc/tests/arc.rs15
-rw-r--r--library/alloc/tests/rc.rs15
-rw-r--r--library/alloc/tests/str.rs26
-rw-r--r--library/core/src/char/convert.rs1
-rw-r--r--library/core/src/char/methods.rs8
-rw-r--r--library/core/src/char/mod.rs6
-rw-r--r--library/core/src/cmp.rs6
-rw-r--r--library/core/src/default.rs2
-rw-r--r--library/core/src/error.rs2
-rw-r--r--library/core/src/fmt/mod.rs1
-rw-r--r--library/core/src/future/mod.rs1
-rw-r--r--library/core/src/hint.rs2
-rw-r--r--library/core/src/intrinsics.rs76
-rw-r--r--library/core/src/lib.rs3
-rw-r--r--library/core/src/macros/mod.rs12
-rw-r--r--library/core/src/marker.rs5
-rw-r--r--library/core/src/mem/maybe_uninit.rs4
-rw-r--r--library/core/src/mem/mod.rs5
-rw-r--r--library/core/src/num/flt2dec/strategy/dragon.rs2
-rw-r--r--library/core/src/num/int_macros.rs32
-rw-r--r--library/core/src/num/nonzero.rs26
-rw-r--r--library/core/src/num/uint_macros.rs16
-rw-r--r--library/core/src/ops/deref.rs2
-rw-r--r--library/core/src/ops/function.rs9
-rw-r--r--library/core/src/ops/index.rs4
-rw-r--r--library/core/src/option.rs2
-rw-r--r--library/core/src/panic.rs1
-rw-r--r--library/core/src/panicking.rs6
-rw-r--r--library/core/src/ptr/const_ptr.rs2
-rw-r--r--library/core/src/ptr/metadata.rs1
-rw-r--r--library/core/src/ptr/mod.rs27
-rw-r--r--library/core/src/ptr/mut_ptr.rs2
-rw-r--r--library/core/src/str/pattern.rs232
-rw-r--r--library/core/src/time.rs8
-rw-r--r--library/core/tests/fmt/float.rs124
-rw-r--r--library/core/tests/num/flt2dec/mod.rs4
-rw-r--r--library/core/tests/option.rs2
-rw-r--r--library/portable-simd/crates/core_simd/src/intrinsics.rs2
-rw-r--r--library/portable-simd/crates/core_simd/src/ops.rs2
-rw-r--r--library/proc_macro/src/lib.rs2
-rw-r--r--library/std/Cargo.toml2
-rw-r--r--library/std/src/net/ip_addr.rs2
-rw-r--r--library/std/src/os/android/net.rs9
-rw-r--r--library/std/src/os/linux/net.rs9
-rw-r--r--library/std/src/os/net/linux_ext/addr.rs64
-rw-r--r--library/std/src/os/net/linux_ext/mod.rs12
-rw-r--r--library/std/src/os/net/linux_ext/tcp.rs (renamed from library/std/src/os/net/tcp.rs)0
-rw-r--r--library/std/src/os/net/linux_ext/tests.rs (renamed from library/std/src/os/net/tests.rs)3
-rw-r--r--library/std/src/os/net/mod.rs9
-rw-r--r--library/std/src/os/unix/net/addr.rs93
-rw-r--r--library/std/src/os/unix/net/tests.rs36
-rw-r--r--library/std/src/os/wasi/io/mod.rs4
-rw-r--r--library/std/src/path.rs13
-rw-r--r--library/std/src/personality/gcc.rs2
-rw-r--r--library/std/src/sync/mod.rs1
-rw-r--r--library/std/src/sync/mpmc/array.rs513
-rw-r--r--library/std/src/sync/mpmc/context.rs155
-rw-r--r--library/std/src/sync/mpmc/counter.rs137
-rw-r--r--library/std/src/sync/mpmc/error.rs46
-rw-r--r--library/std/src/sync/mpmc/list.rs638
-rw-r--r--library/std/src/sync/mpmc/mod.rs430
-rw-r--r--library/std/src/sync/mpmc/select.rs71
-rw-r--r--library/std/src/sync/mpmc/utils.rs144
-rw-r--r--library/std/src/sync/mpmc/waker.rs204
-rw-r--r--library/std/src/sync/mpmc/zero.rs318
-rw-r--r--library/std/src/sync/mpsc/blocking.rs82
-rw-r--r--library/std/src/sync/mpsc/cache_aligned.rs25
-rw-r--r--library/std/src/sync/mpsc/mod.rs458
-rw-r--r--library/std/src/sync/mpsc/mpsc_queue.rs124
-rw-r--r--library/std/src/sync/mpsc/mpsc_queue/tests.rs47
-rw-r--r--library/std/src/sync/mpsc/oneshot.rs315
-rw-r--r--library/std/src/sync/mpsc/shared.rs501
-rw-r--r--library/std/src/sync/mpsc/spsc_queue.rs244
-rw-r--r--library/std/src/sync/mpsc/spsc_queue/tests.rs102
-rw-r--r--library/std/src/sync/mpsc/stream.rs457
-rw-r--r--library/std/src/sync/mpsc/sync.rs495
-rw-r--r--library/std/src/sync/mpsc/tests.rs15
-rw-r--r--library/std/src/thread/local/tests.rs66
-rw-r--r--library/std/src/time.rs2
-rw-r--r--library/unwind/Cargo.toml2
-rw-r--r--library/unwind/build.rs24
-rw-r--r--library/unwind/src/lib.rs24
-rw-r--r--library/unwind/src/libunwind.rs10
-rw-r--r--src/bootstrap/builder.rs261
-rw-r--r--src/bootstrap/channel.rs3
-rw-r--r--src/bootstrap/compile.rs5
-rw-r--r--src/bootstrap/config.rs314
-rw-r--r--src/bootstrap/dist.rs4
-rw-r--r--src/bootstrap/doc.rs6
-rw-r--r--src/bootstrap/download.rs519
-rw-r--r--src/bootstrap/lib.rs101
-rw-r--r--src/bootstrap/native.rs84
-rw-r--r--src/bootstrap/sanity.rs2
-rw-r--r--src/bootstrap/tarball.rs2
-rw-r--r--src/bootstrap/util.rs8
-rw-r--r--src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile9
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh26
-rw-r--r--src/ci/docker/scripts/fuchsia-test-runner.py1041
-rw-r--r--src/doc/rustc/src/codegen-options/index.md4
-rw-r--r--src/doc/rustc/src/platform-support/fuchsia.md62
-rw-r--r--src/librustdoc/clean/mod.rs96
-rw-r--r--src/librustdoc/doctest.rs2
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css9
-rw-r--r--src/librustdoc/html/static/js/main.js6
-rw-r--r--src/librustdoc/html/static/js/search.js1
-rw-r--r--src/librustdoc/html/static_files.rs8
-rw-r--r--src/librustdoc/json/conversions.rs2
-rw-r--r--src/librustdoc/json/mod.rs2
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs4
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs9
-rw-r--r--src/rustdoc-json-types/lib.rs4
-rw-r--r--src/stage0.json676
-rw-r--r--src/test/debuginfo/lexical-scope-in-if-let.rs8
-rw-r--r--src/test/incremental/issue-101518.rs31
-rw-r--r--src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff40
-rw-r--r--src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir28
-rw-r--r--src/test/mir-opt/const_prop/aggregate.rs1
-rw-r--r--src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff23
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff17
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff17
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir27
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir27
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff72
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff72
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.rs2
-rw-r--r--src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot2
-rw-r--r--src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot8
-rw-r--r--src/test/mir-opt/dataflow-const-prop/cast.main.DataflowConstProp.diff37
-rw-r--r--src/test/mir-opt/dataflow-const-prop/cast.rs7
-rw-r--r--src/test/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff80
-rw-r--r--src/test/mir-opt/dataflow-const-prop/checked.rs13
-rw-r--r--src/test/mir-opt/dataflow-const-prop/enum.main.DataflowConstProp.diff61
-rw-r--r--src/test/mir-opt/dataflow-const-prop/enum.rs13
-rw-r--r--src/test/mir-opt/dataflow-const-prop/if.main.DataflowConstProp.diff112
-rw-r--r--src/test/mir-opt/dataflow-const-prop/if.rs11
-rw-r--r--src/test/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff45
-rw-r--r--src/test/mir-opt/dataflow-const-prop/inherit_overflow.rs8
-rw-r--r--src/test/mir-opt/dataflow-const-prop/issue_81605.f.DataflowConstProp.diff35
-rw-r--r--src/test/mir-opt/dataflow-const-prop/issue_81605.rs10
-rw-r--r--src/test/mir-opt/dataflow-const-prop/ref_without_sb.main.DataflowConstProp.diff55
-rw-r--r--src/test/mir-opt/dataflow-const-prop/ref_without_sb.rs17
-rw-r--r--src/test/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff44
-rw-r--r--src/test/mir-opt/dataflow-const-prop/repr_transparent.rs12
-rw-r--r--src/test/mir-opt/dataflow-const-prop/self_assign.main.DataflowConstProp.diff46
-rw-r--r--src/test/mir-opt/dataflow-const-prop/self_assign.rs12
-rw-r--r--src/test/mir-opt/dataflow-const-prop/self_assign_add.main.DataflowConstProp.diff23
-rw-r--r--src/test/mir-opt/dataflow-const-prop/self_assign_add.rs8
-rw-r--r--src/test/mir-opt/dataflow-const-prop/sibling_ptr.main.DataflowConstProp.diff56
-rw-r--r--src/test/mir-opt/dataflow-const-prop/sibling_ptr.rs11
-rw-r--r--src/test/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff52
-rw-r--r--src/test/mir-opt/dataflow-const-prop/struct.rs11
-rw-r--r--src/test/mir-opt/dataflow-const-prop/terminator.main.DataflowConstProp.diff40
-rw-r--r--src/test/mir-opt/dataflow-const-prop/terminator.rs10
-rw-r--r--src/test/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.diff75
-rw-r--r--src/test/mir-opt/dataflow-const-prop/tuple.rs9
-rw-r--r--src/test/mir-opt/issue_101973.inner.ConstProp.diff3
-rw-r--r--src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff19
-rw-r--r--src/test/mir-opt/lower_intrinsics_e2e.f_u64.PreCodegen.after.mir7
-rw-r--r--src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff50
-rw-r--r--src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff45
-rw-r--r--src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff47
-rw-r--r--src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff87
-rw-r--r--src/test/mir-opt/sroa.rs88
-rw-r--r--src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff34
-rw-r--r--src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff24
-rw-r--r--src/test/run-make-fulldeps/split-debuginfo/Makefile210
-rw-r--r--src/test/run-make-fulldeps/split-debuginfo/baz.rs1
-rw-r--r--src/test/rustdoc-gui/help-page.goml2
-rw-r--r--src/test/rustdoc-gui/search-keyboard.goml28
-rw-r--r--src/test/rustdoc-gui/type-declation-overflow.goml17
-rw-r--r--src/test/rustdoc/intra-doc/issue-104145.rs14
-rw-r--r--src/test/rustdoc/issue-102154.rs13
-rw-r--r--src/test/rustdoc/multiple-import-levels.rs34
-rw-r--r--src/test/ui-fulldeps/uninit_mask.rs28
-rw-r--r--src/test/ui/array-slice-vec/vec-macro-with-comma-only.stderr2
-rw-r--r--src/test/ui/associated-consts/defaults-cyclic-fail.stderr8
-rw-r--r--src/test/ui/associated-consts/defaults-not-assumed-fail.rs3
-rw-r--r--src/test/ui/associated-consts/defaults-not-assumed-fail.stderr28
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr4
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr4
-rw-r--r--src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr4
-rw-r--r--src/test/ui/associated-inherent-types/assoc-inherent-private.rs23
-rw-r--r--src/test/ui/associated-inherent-types/assoc-inherent-private.stderr21
-rw-r--r--src/test/ui/associated-inherent-types/assoc-inherent-unstable.rs6
-rw-r--r--src/test/ui/associated-inherent-types/assoc-inherent-unstable.stderr11
-rw-r--r--src/test/ui/associated-inherent-types/auxiliary/assoc-inherent-unstable.rs11
-rw-r--r--src/test/ui/async-await/track-caller/panic-track-caller.rs76
-rw-r--r--src/test/ui/borrowck/async-reference-generality.rs35
-rw-r--r--src/test/ui/borrowck/async-reference-generality.stderr27
-rw-r--r--src/test/ui/borrowck/issue-81899.rs5
-rw-r--r--src/test/ui/borrowck/issue-81899.stderr6
-rw-r--r--src/test/ui/borrowck/issue-88434-minimal-example.rs3
-rw-r--r--src/test/ui/borrowck/issue-88434-minimal-example.stderr6
-rw-r--r--src/test/ui/borrowck/issue-88434-removal-index-should-be-less.rs3
-rw-r--r--src/test/ui/borrowck/issue-88434-removal-index-should-be-less.stderr6
-rw-r--r--src/test/ui/chalkify/closure.rs4
-rw-r--r--src/test/ui/chalkify/closure.stderr88
-rw-r--r--src/test/ui/chalkify/trait-objects.rs3
-rw-r--r--src/test/ui/chalkify/trait-objects.stderr32
-rw-r--r--src/test/ui/codemap_tests/unicode_2.stderr12
-rw-r--r--src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr2
-rw-r--r--src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr4
-rw-r--r--src/test/ui/coherence/coherence-impls-copy.stderr2
-rw-r--r--src/test/ui/coherence/coherence-overlap-issue-23516.stderr4
-rw-r--r--src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr4
-rw-r--r--src/test/ui/coherence/coherence-wasm-bindgen.stderr4
-rw-r--r--src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr4
-rw-r--r--src/test/ui/coherence/coherence_copy_like_err_struct.stderr4
-rw-r--r--src/test/ui/coherence/inter-crate-ambiguity-causes-notes.stderr2
-rw-r--r--src/test/ui/const-generics/min_const_generics/macro-fail.stderr6
-rw-r--r--src/test/ui/consts/const-err-late.rs4
-rw-r--r--src/test/ui/consts/const-err-late.stderr34
-rw-r--r--src/test/ui/consts/const-err-multi.rs6
-rw-r--r--src/test/ui/consts/const-err-multi.stderr14
-rw-r--r--src/test/ui/consts/const-eval/erroneous-const.rs2
-rw-r--r--src/test/ui/consts/const-eval/erroneous-const.stderr10
-rw-r--r--src/test/ui/consts/const-eval/erroneous-const2.rs2
-rw-r--r--src/test/ui/consts/const-eval/erroneous-const2.stderr6
-rw-r--r--src/test/ui/consts/const-eval/format.rs4
-rw-r--r--src/test/ui/consts/const-eval/format.stderr63
-rw-r--r--src/test/ui/consts/const-eval/issue-44578.rs3
-rw-r--r--src/test/ui/consts/const-eval/issue-44578.stderr28
-rw-r--r--src/test/ui/consts/const-eval/issue-50814-2.rs2
-rw-r--r--src/test/ui/consts/const-eval/issue-50814-2.stderr6
-rw-r--r--src/test/ui/consts/const-eval/issue-50814.rs8
-rw-r--r--src/test/ui/consts/const-eval/issue-50814.stderr8
-rw-r--r--src/test/ui/consts/const-eval/panic-assoc-never-type.rs3
-rw-r--r--src/test/ui/consts/const-eval/panic-assoc-never-type.stderr12
-rw-r--r--src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr28
-rw-r--r--src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr28
-rw-r--r--src/test/ui/consts/const-eval/ub-ref-ptr.rs2
-rw-r--r--src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr14
-rw-r--r--src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr14
-rw-r--r--src/test/ui/consts/const-eval/ub-wide-ptr.rs6
-rw-r--r--src/test/ui/consts/const-eval/union-const-eval-field.rs1
-rw-r--r--src/test/ui/consts/const-eval/union-const-eval-field.stderr12
-rw-r--r--src/test/ui/consts/const-float-bits-reject-conv.rs30
-rw-r--r--src/test/ui/consts/const-float-bits-reject-conv.stderr70
-rw-r--r--src/test/ui/consts/const-integer-bool-ops.rs20
-rw-r--r--src/test/ui/consts/const-integer-bool-ops.stderr45
-rw-r--r--src/test/ui/consts/const-len-underflow-separate-spans.rs2
-rw-r--r--src/test/ui/consts/const-len-underflow-separate-spans.stderr6
-rw-r--r--src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr10
-rw-r--r--src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr10
-rw-r--r--src/test/ui/consts/const-mut-refs/issue-76510.rs2
-rw-r--r--src/test/ui/consts/const-tup-index-span.rs2
-rw-r--r--src/test/ui/consts/const-tup-index-span.stderr9
-rw-r--r--src/test/ui/consts/extra-const-ub/detect-extra-ub.rs2
-rw-r--r--src/test/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr6
-rw-r--r--src/test/ui/consts/invalid-inline-const-in-match-arm.rs9
-rw-r--r--src/test/ui/consts/invalid-inline-const-in-match-arm.stderr12
-rw-r--r--src/test/ui/consts/invalid-union.32bit.stderr20
-rw-r--r--src/test/ui/consts/invalid-union.64bit.stderr20
-rw-r--r--src/test/ui/consts/invalid-union.rs3
-rw-r--r--src/test/ui/consts/issue-36163.stderr4
-rw-r--r--src/test/ui/consts/issue-54954.rs4
-rw-r--r--src/test/ui/consts/issue-54954.stderr14
-rw-r--r--src/test/ui/consts/issue-56164.rs1
-rw-r--r--src/test/ui/consts/issue-56164.stderr17
-rw-r--r--src/test/ui/consts/issue-66693.rs1
-rw-r--r--src/test/ui/consts/issue-66693.stderr13
-rw-r--r--src/test/ui/consts/miri_unleashed/assoc_const.rs2
-rw-r--r--src/test/ui/consts/miri_unleashed/assoc_const.stderr18
-rw-r--r--src/test/ui/consts/miri_unleashed/assoc_const_2.rs2
-rw-r--r--src/test/ui/consts/miri_unleashed/assoc_const_2.stderr18
-rw-r--r--src/test/ui/consts/uninhabited-const-issue-61744.rs2
-rw-r--r--src/test/ui/consts/uninhabited-const-issue-61744.stderr18
-rw-r--r--src/test/ui/dyn-star/issue-102430.rs32
-rw-r--r--src/test/ui/editions/edition-keywords-2015-2015-parsing.stderr12
-rw-r--r--src/test/ui/editions/edition-keywords-2015-2018-parsing.stderr12
-rw-r--r--src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr12
-rw-r--r--src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr12
-rw-r--r--src/test/ui/empty/empty-comment.stderr6
-rw-r--r--src/test/ui/error-codes/e0119/conflict-with-std.stderr6
-rw-r--r--src/test/ui/error-codes/e0119/issue-23563.stderr2
-rw-r--r--src/test/ui/error-codes/e0119/issue-27403.stderr2
-rw-r--r--src/test/ui/error-codes/e0119/so-37347311.stderr2
-rw-r--r--src/test/ui/fail-simple.stderr2
-rw-r--r--src/test/ui/fmt/format-raw-string-error.rs3
-rw-r--r--src/test/ui/fmt/format-raw-string-error.stderr10
-rw-r--r--src/test/ui/fmt/issue-104142.rs6
-rw-r--r--src/test/ui/fmt/issue-104142.stderr10
-rw-r--r--src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.rs53
-rw-r--r--src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.stderr21
-rw-r--r--src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.rs23
-rw-r--r--src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr18
-rw-r--r--src/test/ui/impl-trait/deduce-signature-from-supertrait.rs15
-rw-r--r--src/test/ui/issues/issue-17252.stderr4
-rw-r--r--src/test/ui/issues/issue-23302-3.stderr8
-rw-r--r--src/test/ui/issues/issue-28568.stderr2
-rw-r--r--src/test/ui/issues/issue-30490.rs2
-rw-r--r--src/test/ui/issues/issue-41394.rs2
-rw-r--r--src/test/ui/issues/issue-41394.stderr9
-rw-r--r--src/test/ui/issues/issue-43355.stderr2
-rw-r--r--src/test/ui/issues/issue-48728.rs2
-rw-r--r--src/test/ui/issues/issue-48728.stderr2
-rw-r--r--src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs2
-rw-r--r--src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr8
-rw-r--r--src/test/ui/issues/issue-7970a.stderr6
-rw-r--r--src/test/ui/lang-items/lang-item-generic-requirements.rs2
-rw-r--r--src/test/ui/lang-items/lang-item-generic-requirements.stderr27
-rw-r--r--src/test/ui/lexer/error-stage.rs80
-rw-r--r--src/test/ui/lexer/error-stage.stderr54
-rw-r--r--src/test/ui/limits/issue-55878.stderr24
-rw-r--r--src/test/ui/lint/lint-incoherent-auto-trait-objects.stderr24
-rw-r--r--src/test/ui/lint/opaque-ty-ffi-normalization-cycle.rs41
-rw-r--r--src/test/ui/lint/opaque-ty-ffi-normalization-cycle.stderr15
-rw-r--r--src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr4
-rw-r--r--src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr4
-rw-r--r--src/test/ui/macros/issue-68060.rs4
-rw-r--r--src/test/ui/macros/issue-68060.stderr18
-rw-r--r--src/test/ui/macros/macro-at-most-once-rep-2015.stderr54
-rw-r--r--src/test/ui/macros/macro-at-most-once-rep-2018.stderr54
-rw-r--r--src/test/ui/macros/macro-non-lifetime.stderr6
-rw-r--r--src/test/ui/macros/missing-comma.stderr36
-rw-r--r--src/test/ui/macros/nonterminal-matching.stderr8
-rw-r--r--src/test/ui/macros/recovery-allowed.rs8
-rw-r--r--src/test/ui/macros/recovery-allowed.stderr10
-rw-r--r--src/test/ui/macros/recovery-forbidden.rs13
-rw-r--r--src/test/ui/macros/trace_faulty_macros.stderr1
-rw-r--r--src/test/ui/never_type/exhaustive_patterns.rs21
-rw-r--r--src/test/ui/never_type/exhaustive_patterns.stderr25
-rw-r--r--src/test/ui/nll/user-annotations/ascribed-type-wf.rs16
-rw-r--r--src/test/ui/on-unimplemented/issue-104140.rs8
-rw-r--r--src/test/ui/on-unimplemented/issue-104140.stderr15
-rw-r--r--src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr12
-rw-r--r--src/test/ui/panic-handler/panic-handler-std.stderr8
-rw-r--r--src/test/ui/parser/bad-lit-suffixes.rs18
-rw-r--r--src/test/ui/parser/bad-lit-suffixes.stderr60
-rw-r--r--src/test/ui/parser/issues/issue-104088.rs26
-rw-r--r--src/test/ui/parser/issues/issue-104088.stderr29
-rw-r--r--src/test/ui/parser/macro/macro-doc-comments-1.stderr6
-rw-r--r--src/test/ui/parser/macro/macro-doc-comments-2.stderr6
-rw-r--r--src/test/ui/parser/recover-fn-ptr-with-generics.rs31
-rw-r--r--src/test/ui/parser/recover-fn-ptr-with-generics.stderr111
-rw-r--r--src/test/ui/parser/slowparse-bstring.rs (renamed from src/test/ui/slowparse-bstring.rs)0
-rw-r--r--src/test/ui/parser/slowparse-string.rs (renamed from src/test/ui/slowparse-string.rs)0
-rw-r--r--src/test/ui/range/issue-54505-no-std.rs6
-rw-r--r--src/test/ui/range/issue-54505-no-std.stderr38
-rw-r--r--src/test/ui/resolve/issue-50599.rs2
-rw-r--r--src/test/ui/resolve/issue-50599.stderr9
-rw-r--r--src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr6
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr6
-rw-r--r--src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr2
-rw-r--r--src/test/ui/specialization/specialization-overlap-negative.stderr2
-rw-r--r--src/test/ui/specialization/specialization-overlap.stderr4
-rw-r--r--src/test/ui/stats/hir-stats.stderr38
-rw-r--r--src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs4
-rw-r--r--src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr15
-rw-r--r--src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed15
-rw-r--r--src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs15
-rw-r--r--src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr19
-rw-r--r--src/test/ui/target-feature/invalid-attribute.stderr72
-rw-r--r--src/test/ui/traits/issue-102989.rs4
-rw-r--r--src/test/ui/traits/issue-102989.stderr31
-rw-r--r--src/test/ui/traits/issue-33140-hack-boundaries.stderr30
-rw-r--r--src/test/ui/traits/issue-33140.stderr8
-rw-r--r--src/test/ui/traits/item-privacy.stderr5
-rw-r--r--src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr2
-rw-r--r--src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr2
-rw-r--r--src/test/ui/traits/object/issue-33140-traitobject-crate.stderr24
-rw-r--r--src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr2
-rw-r--r--src/test/ui/traits/suggest-fully-qualified-closure.rs24
-rw-r--r--src/test/ui/traits/suggest-fully-qualified-closure.stderr34
-rw-r--r--src/test/ui/type/type-dependent-def-issue-49241.rs2
-rw-r--r--src/test/ui/type/type-dependent-def-issue-49241.stderr9
-rw-r--r--src/test/ui/typeck/issue-103899.rs33
-rw-r--r--src/test/ui/typeck/issue-103899.stderr12
-rw-r--r--src/test/ui/underscore-ident-matcher.stderr6
-rw-r--r--src/test/ui/use/use-crate-self.rs4
-rw-r--r--src/test/ui/use/use-crate-self.stderr8
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/int_plus_one.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/octal_escapes.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/precedence.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_rounding.rs18
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/numeric_literal.rs6
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.stderr6
-rw-r--r--src/tools/lint-docs/src/lib.rs6
-rw-r--r--src/tools/miri/.github/workflows/ci.yml6
-rw-r--r--src/tools/miri/CONTRIBUTING.md33
-rw-r--r--src/tools/miri/Cargo.lock116
-rw-r--r--src/tools/miri/Cargo.toml2
-rw-r--r--src/tools/miri/README.md13
-rw-r--r--src/tools/miri/cargo-miri/Cargo.lock4
-rw-r--r--src/tools/miri/cargo-miri/Cargo.toml2
-rw-r--r--src/tools/miri/cargo-miri/src/setup.rs30
-rwxr-xr-xsrc/tools/miri/miri65
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs34
-rw-r--r--src/tools/miri/src/concurrency/sync.rs4
-rw-r--r--src/tools/miri/src/concurrency/thread.rs3
-rw-r--r--src/tools/miri/src/diagnostics.rs196
-rw-r--r--src/tools/miri/src/helpers.rs20
-rw-r--r--src/tools/miri/src/lib.rs1
-rw-r--r--src/tools/miri/src/machine.rs28
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs46
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs215
-rw-r--r--src/tools/miri/src/stacked_borrows/mod.rs2
-rw-r--r--src/tools/miri/src/tag_gc.rs2
-rw-r--r--src/tools/miri/tests/compiletest.rs6
-rw-r--r--src/tools/miri/tests/fail/copy_half_a_pointer.rs21
-rw-r--r--src/tools/miri/tests/fail/copy_half_a_pointer.stderr14
-rw-r--r--src/tools/miri/tests/fail/erroneous_const.rs2
-rw-r--r--src/tools/miri/tests/fail/erroneous_const.stderr15
-rw-r--r--src/tools/miri/tests/fail/memleak_rc.stderr11
-rw-r--r--src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.rs (renamed from src/tools/miri/tests/fail/pointer_partial_overwrite.rs)7
-rw-r--r--src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr (renamed from src/tools/miri/tests/fail/pointer_partial_overwrite.stderr)4
-rw-r--r--src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs32
-rw-r--r--src/tools/miri/tests/fail/uninit_buffer_with_provenance.stderr32
-rw-r--r--src/tools/miri/tests/pass/miri-alloc.rs29
-rw-r--r--src/tools/miri/tests/pass/provenance.rs139
-rw-r--r--src/tools/rustc-workspace-hack/Cargo.toml2
-rw-r--r--src/tools/rustfmt/src/attr.rs6
-rw-r--r--src/tools/rustfmt/src/expr.rs41
-rw-r--r--src/tools/tidy/src/deps.rs1
-rw-r--r--src/tools/tidy/src/style.rs2
-rw-r--r--src/tools/x/src/main.rs9
-rw-r--r--triagebot.toml2
645 files changed, 16422 insertions, 8797 deletions
diff --git a/.reuse/dep5 b/.reuse/dep5
index e040f73b9e1..5135f92a9d8 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -6,11 +6,6 @@ Files: *
 Copyright: The Rust Project Developers (see https://thanks.rust-lang.org)
 License: MIT or Apache-2.0
 
-Files: library/std/src/sync/mpsc/mpsc_queue.rs
-       library/std/src/sync/mpsc/spsc_queue.rs
-Copyright: 2010-2011 Dmitry Vyukov
-License: BSD-2-Clause
-
 Files: src/librustdoc/html/static/fonts/FiraSans*
 Copyright: 2014, Mozilla Foundation, 2014, Telefonica S.A.
 License: OFL-1.1
diff --git a/Cargo.lock b/Cargo.lock
index c105d04c1f4..c867bb4ceaa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -215,6 +215,18 @@ dependencies = [
 ]
 
 [[package]]
+name = "bstr"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd"
+dependencies = [
+ "memchr",
+ "once_cell",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
 name = "build-manifest"
 version = "0.1.0"
 dependencies = [
@@ -423,7 +435,7 @@ dependencies = [
 
 [[package]]
 name = "cargo-util"
-version = "0.2.2"
+version = "0.2.3"
 dependencies = [
  "anyhow",
  "core-foundation",
@@ -473,9 +485,9 @@ version = "0.1.0"
 
 [[package]]
 name = "cc"
-version = "1.0.73"
+version = "1.0.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
 dependencies = [
  "jobserver",
 ]
@@ -502,9 +514,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-derive"
-version = "0.80.0"
+version = "0.87.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0001adf0cf12361e08b65e1898ea138f8f77d8f5177cbf29b6b3b3532252bd6"
+checksum = "d552b2fa341f5fc35c6b917b1d289d3c3a34d0b74e579390ea6192d6152a8cdb"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -514,9 +526,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-engine"
-version = "0.80.0"
+version = "0.87.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c44ee96f2d67cb5193d1503f185db1abad9933a1c6e6b4169c176f90baecd393"
+checksum = "7e54ac43048cb31c470d7b3e3acd409090ef4a5abddfe02455187aebc3d6879f"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -527,9 +539,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.80.0"
+version = "0.87.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92d8a95548f23618fda86426e4304e563ec2bb7ba0216139f0748d63c107b5f1"
+checksum = "43aa55deff4e7fbdb09fa014543372f2c95a06835ac487b9ce57b5099b950838"
 dependencies = [
  "bitflags",
  "chalk-derive",
@@ -538,9 +550,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.80.0"
+version = "0.87.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f37f492dacfafe2e21319b80827da2779932909bb392f0cc86b2bd5c07c1b4e1"
+checksum = "61213deefc36ba265ad01c4d997e18bcddf7922862a4594a47ca4575afb3dab4"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -900,7 +912,7 @@ dependencies = [
 
 [[package]]
 name = "crates-io"
-version = "0.34.0"
+version = "0.35.0"
 dependencies = [
  "anyhow",
  "curl",
@@ -920,24 +932,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "crossbeam"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845"
-dependencies = [
- "cfg-if 1.0.0",
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-epoch",
- "crossbeam-queue",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "crossbeam-channel"
-version = "0.5.4"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
+checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-utils",
@@ -968,16 +966,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "crossbeam-queue"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
-dependencies = [
- "cfg-if 1.0.0",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "crossbeam-utils"
 version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1261,6 +1249,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 
 [[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
 name = "filetime"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1584,7 +1581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
 dependencies = [
  "aho-corasick",
- "bstr",
+ "bstr 0.2.17",
  "fnv",
  "log",
  "regex",
@@ -2341,9 +2338,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.12.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
 
 [[package]]
 name = "opener"
@@ -2351,7 +2348,7 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
 dependencies = [
- "bstr",
+ "bstr 0.2.17",
  "winapi",
 ]
 
@@ -3008,9 +3005,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-build-sysroot"
-version = "0.3.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec5f3689b6c560d6a3a17fcbe54204cd870b4fcf46342d60de16715b660d2c92"
+checksum = "20c4b4625eeb148cccf82d5e9b90ad7fab3b11a0204cf75cc7fa04981a0fdffd"
 dependencies = [
  "anyhow",
  "rustc_version",
@@ -3098,9 +3095,10 @@ dependencies = [
 name = "rustc-workspace-hack"
 version = "1.0.0"
 dependencies = [
- "bstr",
+ "bstr 0.2.17",
  "clap 3.2.20",
  "libz-sys",
+ "rand 0.8.5",
  "regex",
  "serde_json",
  "syn",
@@ -4767,13 +4765,13 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.2.0"
+version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
 dependencies = [
  "cfg-if 1.0.0",
+ "fastrand",
  "libc",
- "rand 0.8.5",
  "redox_syscall",
  "remove_dir_all",
  "winapi",
@@ -5119,20 +5117,22 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
 
 [[package]]
 name = "ui_test"
-version = "0.3.1"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d1f546a5883ae78da735bba529ec1116661e2f73582f23920d994dc97da3a22"
+checksum = "bf4559da3fe6b481f8674a29379677cb9606cd6f75fc254a2c9834c55638503d"
 dependencies = [
+ "bstr 1.0.1",
  "cargo_metadata 0.15.0",
  "color-eyre",
  "colored",
- "crossbeam",
+ "crossbeam-channel",
  "diff",
  "lazy_static",
  "regex",
  "rustc_version",
  "serde",
  "serde_json",
+ "tempfile",
 ]
 
 [[package]]
diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt
deleted file mode 100644
index 5f662b354cd..00000000000
--- a/LICENSES/BSD-2-Clause.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-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/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index c999b06b0ab..5d9d0a5feca 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -392,15 +392,7 @@ pub struct Generics {
 impl Default for Generics {
     /// Creates an instance of `Generics`.
     fn default() -> Generics {
-        Generics {
-            params: Vec::new(),
-            where_clause: WhereClause {
-                has_where_token: false,
-                predicates: Vec::new(),
-                span: DUMMY_SP,
-            },
-            span: DUMMY_SP,
-        }
+        Generics { params: Vec::new(), where_clause: Default::default(), span: DUMMY_SP }
     }
 }
 
@@ -415,6 +407,12 @@ pub struct WhereClause {
     pub span: Span,
 }
 
+impl Default for WhereClause {
+    fn default() -> WhereClause {
+        WhereClause { has_where_token: false, predicates: Vec::new(), span: DUMMY_SP }
+    }
+}
+
 /// A single predicate in a where-clause.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum WherePredicate {
@@ -1334,7 +1332,7 @@ pub enum ExprKind {
     /// A unary operation (e.g., `!x`, `*x`).
     Unary(UnOp, P<Expr>),
     /// A literal (e.g., `1`, `"foo"`).
-    Lit(Lit),
+    Lit(token::Lit),
     /// A cast (e.g., `foo as f64`).
     Cast(P<Expr>, P<Ty>),
     /// A type ascription (e.g., `42: usize`).
@@ -1700,16 +1698,12 @@ pub struct StrLit {
 }
 
 impl StrLit {
-    pub fn as_lit(&self) -> Lit {
+    pub fn as_token_lit(&self) -> token::Lit {
         let token_kind = match self.style {
             StrStyle::Cooked => token::Str,
             StrStyle::Raw(n) => token::StrRaw(n),
         };
-        Lit {
-            token_lit: token::Lit::new(token_kind, self.symbol, self.suffix),
-            span: self.span,
-            kind: LitKind::Str(self.symbol_unescaped, self.style),
-        }
+        token::Lit::new(token_kind, self.symbol, self.suffix)
     }
 }
 
@@ -1735,9 +1729,10 @@ pub enum LitFloatType {
     Unsuffixed,
 }
 
-/// Literal kind.
-///
-/// E.g., `"foo"`, `42`, `12.34`, or `bool`.
+/// Note that the entire literal (including the suffix) is considered when
+/// deciding the `LitKind`. This means that float literals like `1f32` are
+/// classified by this type as `Float`. This is different to `token::LitKind`
+/// which does *not* consider the suffix.
 #[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq, HashStable_Generic)]
 pub enum LitKind {
     /// A string literal (`"foo"`). The symbol is unescaped, and so may differ
@@ -1751,10 +1746,11 @@ pub enum LitKind {
     Char(char),
     /// An integer literal (`1`).
     Int(u128, LitIntType),
-    /// A float literal (`1f64` or `1E10f64`). Stored as a symbol rather than
-    /// `f64` so that `LitKind` can impl `Eq` and `Hash`.
+    /// A float literal (`1.0`, `1f64` or `1E10f64`). The pre-suffix part is
+    /// stored as a symbol rather than `f64` so that `LitKind` can impl `Eq`
+    /// and `Hash`.
     Float(Symbol, LitFloatType),
-    /// A boolean literal.
+    /// A boolean literal (`true`, `false`).
     Bool(bool),
     /// Placeholder for a literal that wasn't well-formed in some way.
     Err,
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 990f4f8f132..07f982b7e86 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -533,7 +533,7 @@ impl MetaItemKind {
             MetaItemKind::NameValue(lit) => {
                 let expr = P(ast::Expr {
                     id: ast::DUMMY_NODE_ID,
-                    kind: ast::ExprKind::Lit(lit.clone()),
+                    kind: ast::ExprKind::Lit(lit.token_lit.clone()),
                     span: lit.span,
                     attrs: ast::AttrVec::new(),
                     tokens: None,
@@ -605,7 +605,7 @@ impl MetaItemKind {
                 MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees())
             }
             Some(TokenTree::Token(token, _)) => {
-                Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
+                Lit::from_token(&token).map(MetaItemKind::NameValue)
             }
             _ => None,
         }
@@ -618,8 +618,10 @@ impl MetaItemKind {
                 MetaItemKind::list_from_tokens(tokens.clone())
             }
             MacArgs::Delimited(..) => None,
-            MacArgs::Eq(_, MacArgsEq::Ast(expr)) => match &expr.kind {
-                ast::ExprKind::Lit(lit) => Some(MetaItemKind::NameValue(lit.clone())),
+            MacArgs::Eq(_, MacArgsEq::Ast(expr)) => match expr.kind {
+                ast::ExprKind::Lit(token_lit) => Some(MetaItemKind::NameValue(
+                    Lit::from_token_lit(token_lit, expr.span).expect("token_lit in from_mac_args"),
+                )),
                 _ => None,
             },
             MacArgs::Eq(_, MacArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
@@ -668,7 +670,7 @@ impl NestedMetaItem {
     {
         match tokens.peek() {
             Some(TokenTree::Token(token, _))
-                if let Ok(lit) = Lit::from_token(token) =>
+                if let Some(lit) = Lit::from_token(token) =>
             {
                 tokens.next();
                 return Some(NestedMetaItem::Literal(lit));
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index f6aac0b55f1..e0ff690e766 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -59,13 +59,17 @@ pub enum Delimiter {
     Invisible,
 }
 
+// Note that the suffix is *not* considered when deciding the `LitKind` in this
+// type. This means that float literals like `1f32` are classified by this type
+// as `Int`. Only upon conversion to `ast::LitKind` will such a literal be
+// given the `Float` kind.
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub enum LitKind {
     Bool, // AST only, must never appear in a `Token`
     Byte,
     Char,
-    Integer,
-    Float,
+    Integer, // e.g. `1`, `1u8`, `1f32`
+    Float,   // e.g. `1.`, `1.0`, `1e3f32`
     Str,
     StrRaw(u8), // raw string delimited by `n` hash symbols
     ByteStr,
@@ -81,6 +85,42 @@ pub struct Lit {
     pub suffix: Option<Symbol>,
 }
 
+impl Lit {
+    pub fn new(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Lit {
+        Lit { kind, symbol, suffix }
+    }
+
+    /// Returns `true` if this is semantically a float literal. This includes
+    /// ones like `1f32` that have an `Integer` kind but a float suffix.
+    pub fn is_semantic_float(&self) -> bool {
+        match self.kind {
+            LitKind::Float => true,
+            LitKind::Integer => match self.suffix {
+                Some(sym) => sym == sym::f32 || sym == sym::f64,
+                None => false,
+            },
+            _ => false,
+        }
+    }
+
+    /// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation.
+    pub fn from_token(token: &Token) -> Option<Lit> {
+        match token.uninterpolate().kind {
+            Ident(name, false) if name.is_bool_lit() => {
+                Some(Lit::new(Bool, name, None))
+            }
+            Literal(token_lit) => Some(token_lit),
+            Interpolated(ref nt)
+                if let NtExpr(expr) | NtLiteral(expr) = &**nt
+                && let ast::ExprKind::Lit(token_lit) = expr.kind =>
+            {
+                Some(token_lit.clone())
+            }
+            _ => None,
+        }
+    }
+}
+
 impl fmt::Display for Lit {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let Lit { kind, symbol, suffix } = *self;
@@ -139,12 +179,6 @@ impl LitKind {
     }
 }
 
-impl Lit {
-    pub fn new(kind: LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Lit {
-        Lit { kind, symbol, suffix }
-    }
-}
-
 pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
     let ident_token = Token::new(Ident(name, is_raw), span);
 
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index e267f8cd100..db2ac9626af 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -8,8 +8,8 @@ use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 use std::ascii;
 
+#[derive(Debug)]
 pub enum LitError {
-    NotLiteral,
     LexerError,
     InvalidSuffix,
     InvalidIntSuffix,
@@ -202,27 +202,10 @@ impl Lit {
         Ok(Lit { token_lit, kind: LitKind::from_token_lit(token_lit)?, span })
     }
 
-    /// Converts arbitrary token into an AST literal.
-    ///
-    /// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation.
-    pub fn from_token(token: &Token) -> Result<Lit, LitError> {
-        let lit = match token.uninterpolate().kind {
-            token::Ident(name, false) if name.is_bool_lit() => {
-                token::Lit::new(token::Bool, name, None)
-            }
-            token::Literal(lit) => lit,
-            token::Interpolated(ref nt) => {
-                if let token::NtExpr(expr) | token::NtLiteral(expr) = &**nt
-                    && let ast::ExprKind::Lit(lit) = &expr.kind
-                {
-                    return Ok(lit.clone());
-                }
-                return Err(LitError::NotLiteral);
-            }
-            _ => return Err(LitError::NotLiteral),
-        };
-
-        Lit::from_token_lit(lit, token.span)
+    /// Converts an arbitrary token into an AST literal.
+    pub fn from_token(token: &Token) -> Option<Lit> {
+        token::Lit::from_token(token)
+            .and_then(|token_lit| Lit::from_token_lit(token_lit, token.span).ok())
     }
 
     /// Attempts to recover an AST literal from semantic literal.
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 9053a5a1d64..da0545ce80c 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -251,7 +251,7 @@ pub trait Visitor<'ast>: Sized {
 macro_rules! walk_list {
     ($visitor: expr, $method: ident, $list: expr $(, $($extra_args: expr),* )?) => {
         {
-            #[cfg_attr(not(bootstrap), allow(for_loops_over_fallibles))]
+            #[allow(for_loops_over_fallibles)]
             for elem in $list {
                 $visitor.$method(elem $(, $($extra_args,)* )?)
             }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index a4ae493af86..01716859a9d 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -14,6 +14,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::definitions::DefPathData;
+use rustc_session::errors::report_lit_error;
 use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::DUMMY_SP;
@@ -84,8 +85,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let ohs = self.lower_expr(ohs);
                     hir::ExprKind::Unary(op, ohs)
                 }
-                ExprKind::Lit(ref l) => {
-                    hir::ExprKind::Lit(respan(self.lower_span(l.span), l.kind.clone()))
+                ExprKind::Lit(token_lit) => {
+                    let lit_kind = match LitKind::from_token_lit(token_lit) {
+                        Ok(lit_kind) => lit_kind,
+                        Err(err) => {
+                            report_lit_error(&self.tcx.sess.parse_sess, err, token_lit, e.span);
+                            LitKind::Err
+                        }
+                    };
+                    hir::ExprKind::Lit(respan(self.lower_span(e.span), lit_kind))
                 }
                 ExprKind::IncludedBytes(ref bytes) => hir::ExprKind::Lit(respan(
                     self.lower_span(e.span),
@@ -635,6 +643,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // `static |_task_context| -> <ret_ty> { body }`:
         let generator_kind = {
             let c = self.arena.alloc(hir::Closure {
+                def_id: self.local_def_id(closure_node_id),
                 binder: hir::ClosureBinder::Default,
                 capture_clause,
                 bound_generic_params: &[],
@@ -646,15 +655,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
             hir::ExprKind::Closure(c)
         };
-        let generator = hir::Expr {
-            hir_id: self.lower_node_id(closure_node_id),
-            kind: generator_kind,
-            span: self.lower_span(span),
+        let parent_has_track_caller = self
+            .attrs
+            .values()
+            .find(|attrs| attrs.into_iter().find(|attr| attr.has_name(sym::track_caller)).is_some())
+            .is_some();
+        let unstable_span =
+            self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
+
+        let hir_id = if parent_has_track_caller {
+            let generator_hir_id = self.lower_node_id(closure_node_id);
+            self.lower_attrs(
+                generator_hir_id,
+                &[Attribute {
+                    kind: AttrKind::Normal(ptr::P(NormalAttr {
+                        item: AttrItem {
+                            path: Path::from_ident(Ident::new(sym::track_caller, span)),
+                            args: MacArgs::Empty,
+                            tokens: None,
+                        },
+                        tokens: None,
+                    })),
+                    id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(),
+                    style: AttrStyle::Outer,
+                    span: unstable_span,
+                }],
+            );
+            generator_hir_id
+        } else {
+            self.lower_node_id(closure_node_id)
         };
 
+        let generator = hir::Expr { hir_id, kind: generator_kind, span: self.lower_span(span) };
+
         // `future::from_generator`:
-        let unstable_span =
-            self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
         let gen_future = self.expr_lang_item_path(
             unstable_span,
             hir::LangItem::FromGenerator,
@@ -887,6 +921,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let fn_decl = self.lower_fn_decl(decl, None, fn_decl_span, FnDeclKind::Closure, None);
 
         let c = self.arena.alloc(hir::Closure {
+            def_id: self.local_def_id(closure_id),
             binder: binder_clause,
             capture_clause,
             bound_generic_params,
@@ -991,6 +1026,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             self.lower_fn_decl(&outer_decl, None, fn_decl_span, FnDeclKind::Closure, None);
 
         let c = self.arena.alloc(hir::Closure {
+            def_id: self.local_def_id(closure_id),
             binder: binder_clause,
             capture_clause,
             bound_generic_params,
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index f1851d7b40e..3a0e5f55ec1 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -307,8 +307,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
     }
 
     fn visit_variant(&mut self, v: &'hir Variant<'hir>) {
-        self.insert(v.span, v.id, Node::Variant(v));
-        self.with_parent(v.id, |this| {
+        self.insert(v.span, v.hir_id, Node::Variant(v));
+        self.with_parent(v.hir_id, |this| {
             // Register the constructor of this variant.
             if let Some(ctor_hir_id) = v.data.ctor_hir_id() {
                 this.insert(v.span, ctor_hir_id, Node::Ctor(&v.data));
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 76316a574ac..795ad113ef2 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -86,7 +86,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             impl_trait_defs: Vec::new(),
             impl_trait_bounds: Vec::new(),
             allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
-            allow_gen_future: Some([sym::gen_future][..].into()),
+            allow_gen_future: Some([sym::gen_future, sym::closure_track_caller][..].into()),
             allow_into_future: Some([sym::into_future][..].into()),
             generics_def_id_map: Default::default(),
         };
@@ -709,11 +709,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
-        let id = self.lower_node_id(v.id);
-        self.lower_attrs(id, &v.attrs);
+        let hir_id = self.lower_node_id(v.id);
+        self.lower_attrs(hir_id, &v.attrs);
         hir::Variant {
-            id,
-            data: self.lower_variant_data(id, &v.data),
+            hir_id,
+            def_id: self.local_def_id(v.id),
+            data: self.lower_variant_data(hir_id, &v.data),
             disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const(e)),
             ident: self.lower_ident(v.ident),
             span: self.lower_span(v.span),
@@ -739,12 +740,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         fields.iter().enumerate().map(|f| self.lower_field_def(f)),
                     ),
                     ctor_id,
+                    self.local_def_id(id),
                 )
             }
             VariantData::Unit(id) => {
                 let ctor_id = self.lower_node_id(id);
                 self.alias_attrs(ctor_id, parent_id);
-                hir::VariantData::Unit(ctor_id)
+                hir::VariantData::Unit(ctor_id, self.local_def_id(id))
             }
         }
     }
@@ -767,6 +769,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         hir::FieldDef {
             span: self.lower_span(f.span),
             hir_id,
+            def_id: self.local_def_id(f.id),
             ident: match f.ident {
                 Some(ident) => self.lower_ident(ident),
                 // FIXME(jseyfried): positional field hygiene.
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index ff29d15f1b5..5ab75b1294b 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -830,8 +830,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             ),
         };
         let hir_id = self.lower_node_id(node_id);
+        let def_id = self.local_def_id(node_id);
         Some(hir::GenericParam {
             hir_id,
+            def_id,
             name,
             span: self.lower_span(ident.span),
             pure_wrt_drop: false,
@@ -959,8 +961,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             MacArgs::Eq(eq_span, MacArgsEq::Ast(ref expr)) => {
                 // In valid code the value always ends up as a single literal. Otherwise, a dummy
                 // literal suffices because the error is handled elsewhere.
-                let lit = if let ExprKind::Lit(lit) = &expr.kind {
-                    lit.clone()
+                let lit = if let ExprKind::Lit(token_lit) = expr.kind {
+                    match Lit::from_token_lit(token_lit, expr.span) {
+                        Ok(lit) => lit,
+                        Err(_err) => Lit {
+                            token_lit: token::Lit::new(token::LitKind::Err, kw::Empty, None),
+                            kind: LitKind::Err,
+                            span: DUMMY_SP,
+                        },
+                    }
                 } else {
                     Lit {
                         token_lit: token::Lit::new(token::LitKind::Err, kw::Empty, None),
@@ -1158,7 +1167,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 let node_id = self.next_node_id();
 
                                 // Add a definition for the in-band const def.
-                                self.create_def(
+                                let def_id = self.create_def(
                                     parent_def_id.def_id,
                                     node_id,
                                     DefPathData::AnonConst,
@@ -1174,6 +1183,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 };
 
                                 let ct = self.with_new_scopes(|this| hir::AnonConst {
+                                    def_id,
                                     hir_id: this.lower_node_id(node_id),
                                     body: this.lower_const_body(path_expr.span, Some(&path_expr)),
                                 });
@@ -1521,6 +1531,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                         hir::GenericParam {
                             hir_id,
+                            def_id: lctx.local_def_id(new_node_id),
                             name,
                             span: lifetime.ident.span,
                             pure_wrt_drop: false,
@@ -1978,6 +1989,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                         hir::GenericParam {
                             hir_id,
+                            def_id: this.local_def_id(new_node_id),
                             name,
                             span: lifetime.ident.span,
                             pure_wrt_drop: false,
@@ -2176,6 +2188,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         self.lower_attrs(hir_id, &param.attrs);
         hir::GenericParam {
             hir_id,
+            def_id: self.local_def_id(param.id),
             name,
             span: self.lower_span(param.span()),
             pure_wrt_drop: self.tcx.sess.contains_name(&param.attrs, sym::may_dangle),
@@ -2280,6 +2293,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // Set the name to `impl Bound1 + Bound2`.
         let param = hir::GenericParam {
             hir_id: self.lower_node_id(node_id),
+            def_id,
             name: ParamName::Plain(self.lower_ident(ident)),
             pure_wrt_drop: false,
             span: self.lower_span(span),
@@ -2340,6 +2354,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_anon_const(&mut self, c: &AnonConst) -> hir::AnonConst {
         self.with_new_scopes(|this| hir::AnonConst {
+            def_id: this.local_def_id(c.id),
             hir_id: this.lower_node_id(c.id),
             body: this.lower_const_body(c.value.span, Some(&c.value)),
         })
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 03664324404..712fb5ac71f 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1051,6 +1051,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl);
                 });
+                walk_list!(self, visit_attribute, &item.attrs);
                 return; // Avoid visiting again.
             }
             ItemKind::Impl(box Impl {
@@ -1168,7 +1169,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 });
                 walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait);
                 walk_list!(self, visit_attribute, &item.attrs);
-                return;
+                return; // Avoid visiting again
             }
             ItemKind::Mod(unsafety, ref mod_kind) => {
                 if let Unsafe::Yes(span) = unsafety {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index b87c6f78d72..5f01f555b30 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -373,8 +373,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     }
 
     fn print_literal(&mut self, lit: &ast::Lit) {
-        self.maybe_print_comment(lit.span.lo());
-        self.word(lit.token_lit.to_string())
+        self.print_token_literal(lit.token_lit, lit.span)
+    }
+
+    fn print_token_literal(&mut self, token_lit: token::Lit, span: Span) {
+        self.maybe_print_comment(span.lo());
+        self.word(token_lit.to_string())
     }
 
     fn print_string(&mut self, st: &str, style: ast::StrStyle) {
@@ -1735,7 +1739,7 @@ impl<'a> State<'a> {
             }
             ast::Extern::Explicit(abi, _) => {
                 self.word_nbsp("extern");
-                self.print_literal(&abi.as_lit());
+                self.print_token_literal(abi.as_token_lit(), abi.span);
                 self.nbsp();
             }
         }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 930276242c3..86f1d6bfecd 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -319,8 +319,8 @@ impl<'a> State<'a> {
             ast::ExprKind::AddrOf(k, m, ref expr) => {
                 self.print_expr_addr_of(k, m, expr);
             }
-            ast::ExprKind::Lit(ref lit) => {
-                self.print_literal(lit);
+            ast::ExprKind::Lit(token_lit) => {
+                self.print_token_literal(token_lit, expr.span);
             }
             ast::ExprKind::IncludedBytes(ref bytes) => {
                 let lit = ast::Lit::from_included_bytes(bytes, expr.span);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 159853c9e24..9c4425701e0 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -207,7 +207,7 @@ impl<'a> State<'a> {
                     s.word("extern");
                 }));
                 if let Some(abi) = nmod.abi {
-                    self.print_literal(&abi.as_lit());
+                    self.print_token_literal(abi.as_token_lit(), abi.span);
                     self.nbsp();
                 }
                 self.bopen();
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index a66ddd27dbb..62c6f958137 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -42,8 +42,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             user_provided_sig = None;
         } else {
             let typeck_results = self.tcx().typeck(mir_def_id);
-            user_provided_sig = typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()).map(
-                |user_provided_poly_sig| {
+            user_provided_sig =
+                typeck_results.user_provided_sigs.get(&mir_def_id).map(|user_provided_poly_sig| {
                     // Instantiate the canonicalized variables from
                     // user-provided signature (e.g., the `_` in the code
                     // above) with fresh variables.
@@ -60,8 +60,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         LateBoundRegionConversionTime::FnCall,
                         poly_sig,
                     )
-                },
-            );
+                });
         }
 
         debug!(?normalized_input_tys, ?body.local_decls);
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index a1051d990b1..a34c17a4258 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -172,7 +172,11 @@ pub fn parse_asm_args<'a>(
             // If it can't possibly expand to a string, provide diagnostics here to include other
             // things it could have been.
             match template.kind {
-                ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {}
+                ast::ExprKind::Lit(token_lit)
+                    if matches!(
+                        token_lit.kind,
+                        token::LitKind::Str | token::LitKind::StrRaw(_)
+                    ) => {}
                 ast::ExprKind::MacCall(..) => {}
                 _ => {
                     let errstr = if is_global_asm {
diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs
index 01454d0e98e..d579616ad1b 100644
--- a/compiler/rustc_builtin_macros/src/concat.rs
+++ b/compiler/rustc_builtin_macros/src/concat.rs
@@ -1,6 +1,7 @@
 use rustc_ast as ast;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_expand::base::{self, DummyResult};
+use rustc_session::errors::report_lit_error;
 use rustc_span::symbol::Symbol;
 
 use std::string::String;
@@ -18,28 +19,28 @@ pub fn expand_concat(
     let mut has_errors = false;
     for e in es {
         match e.kind {
-            ast::ExprKind::Lit(ref lit) => match lit.kind {
-                ast::LitKind::Str(ref s, _) | ast::LitKind::Float(ref s, _) => {
+            ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
+                Ok(ast::LitKind::Str(ref s, _) | ast::LitKind::Float(ref s, _)) => {
                     accumulator.push_str(s.as_str());
                 }
-                ast::LitKind::Char(c) => {
+                Ok(ast::LitKind::Char(c)) => {
                     accumulator.push(c);
                 }
-                ast::LitKind::Int(
-                    i,
-                    ast::LitIntType::Unsigned(_)
-                    | ast::LitIntType::Signed(_)
-                    | ast::LitIntType::Unsuffixed,
-                ) => {
+                Ok(ast::LitKind::Int(i, _)) => {
                     accumulator.push_str(&i.to_string());
                 }
-                ast::LitKind::Bool(b) => {
+                Ok(ast::LitKind::Bool(b)) => {
                     accumulator.push_str(&b.to_string());
                 }
-                ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..) => {
+                Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
                     cx.span_err(e.span, "cannot concatenate a byte string literal");
+                    has_errors = true;
+                }
+                Ok(ast::LitKind::Err) => {
+                    has_errors = true;
                 }
-                ast::LitKind::Err => {
+                Err(err) => {
+                    report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span);
                     has_errors = true;
                 }
             },
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
index 4886ca786a5..87658e60e9d 100644
--- a/compiler/rustc_builtin_macros/src/concat_bytes.rs
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -2,18 +2,21 @@ use rustc_ast as ast;
 use rustc_ast::{ptr::P, tokenstream::TokenStream};
 use rustc_errors::Applicability;
 use rustc_expand::base::{self, DummyResult};
+use rustc_span::Span;
 
 /// Emits errors for literal expressions that are invalid inside and outside of an array.
-fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_nested: bool) {
-    let ast::ExprKind::Lit(lit) = &expr.kind else {
-        unreachable!();
-    };
-    match lit.kind {
-        ast::LitKind::Char(_) => {
-            let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals");
-            if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
+fn invalid_type_err(
+    cx: &mut base::ExtCtxt<'_>,
+    token_lit: ast::token::Lit,
+    span: Span,
+    is_nested: bool,
+) {
+    match ast::LitKind::from_token_lit(token_lit) {
+        Ok(ast::LitKind::Char(_)) => {
+            let mut err = cx.struct_span_err(span, "cannot concatenate character literals");
+            if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) {
                 err.span_suggestion(
-                    expr.span,
+                    span,
                     "try using a byte character",
                     format!("b{}", snippet),
                     Applicability::MachineApplicable,
@@ -21,13 +24,13 @@ fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_ne
                 .emit();
             }
         }
-        ast::LitKind::Str(_, _) => {
-            let mut err = cx.struct_span_err(expr.span, "cannot concatenate string literals");
+        Ok(ast::LitKind::Str(_, _)) => {
+            let mut err = cx.struct_span_err(span, "cannot concatenate string literals");
             // suggestion would be invalid if we are nested
             if !is_nested {
-                if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
+                if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) {
                     err.span_suggestion(
-                        expr.span,
+                        span,
                         "try using a byte string",
                         format!("b{}", snippet),
                         Applicability::MachineApplicable,
@@ -36,18 +39,18 @@ fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_ne
             }
             err.emit();
         }
-        ast::LitKind::Float(_, _) => {
-            cx.span_err(expr.span, "cannot concatenate float literals");
+        Ok(ast::LitKind::Float(_, _)) => {
+            cx.span_err(span, "cannot concatenate float literals");
         }
-        ast::LitKind::Bool(_) => {
-            cx.span_err(expr.span, "cannot concatenate boolean literals");
+        Ok(ast::LitKind::Bool(_)) => {
+            cx.span_err(span, "cannot concatenate boolean literals");
         }
-        ast::LitKind::Err => {}
-        ast::LitKind::Int(_, _) if !is_nested => {
-            let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals");
-            if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) {
+        Ok(ast::LitKind::Err) => {}
+        Ok(ast::LitKind::Int(_, _)) if !is_nested => {
+            let mut err = cx.struct_span_err(span, "cannot concatenate numeric literals");
+            if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) {
                 err.span_suggestion(
-                    expr.span,
+                    span,
                     "try wrapping the number in an array",
                     format!("[{}]", snippet),
                     Applicability::MachineApplicable,
@@ -55,15 +58,15 @@ fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_ne
             }
             err.emit();
         }
-        ast::LitKind::Int(
+        Ok(ast::LitKind::Int(
             val,
             ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
-        ) => {
+        )) => {
             assert!(val > u8::MAX.into()); // must be an error
-            cx.span_err(expr.span, "numeric literal is out of bounds");
+            cx.span_err(span, "numeric literal is out of bounds");
         }
-        ast::LitKind::Int(_, _) => {
-            cx.span_err(expr.span, "numeric literal is not a `u8`");
+        Ok(ast::LitKind::Int(_, _)) => {
+            cx.span_err(span, "numeric literal is not a `u8`");
         }
         _ => unreachable!(),
     }
@@ -83,14 +86,14 @@ fn handle_array_element(
             *has_errors = true;
             None
         }
-        ast::ExprKind::Lit(ref lit) => match lit.kind {
-            ast::LitKind::Int(
+        ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
+            Ok(ast::LitKind::Int(
                 val,
                 ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
-            ) if val <= u8::MAX.into() => Some(val as u8),
+            )) if val <= u8::MAX.into() => Some(val as u8),
 
-            ast::LitKind::Byte(val) => Some(val),
-            ast::LitKind::ByteStr(_) => {
+            Ok(ast::LitKind::Byte(val)) => Some(val),
+            Ok(ast::LitKind::ByteStr(_)) => {
                 if !*has_errors {
                     cx.struct_span_err(expr.span, "cannot concatenate doubly nested array")
                         .note("byte strings are treated as arrays of bytes")
@@ -102,7 +105,7 @@ fn handle_array_element(
             }
             _ => {
                 if !*has_errors {
-                    invalid_type_err(cx, expr, true);
+                    invalid_type_err(cx, token_lit, expr.span, true);
                 }
                 *has_errors = true;
                 None
@@ -148,9 +151,9 @@ pub fn expand_concat_bytes(
                 }
             }
             ast::ExprKind::Repeat(ref expr, ref count) => {
-                if let ast::ExprKind::Lit(ast::Lit {
-                    kind: ast::LitKind::Int(count_val, _), ..
-                }) = count.value.kind
+                if let ast::ExprKind::Lit(token_lit) = count.value.kind
+                && let Ok(ast::LitKind::Int(count_val, _)) =
+                    ast::LitKind::from_token_lit(token_lit)
                 {
                     if let Some(elem) =
                         handle_array_element(cx, &mut has_errors, &mut missing_literals, expr)
@@ -163,16 +166,16 @@ pub fn expand_concat_bytes(
                     cx.span_err(count.value.span, "repeat count is not a positive number");
                 }
             }
-            ast::ExprKind::Lit(ref lit) => match lit.kind {
-                ast::LitKind::Byte(val) => {
+            ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
+                Ok(ast::LitKind::Byte(val)) => {
                     accumulator.push(val);
                 }
-                ast::LitKind::ByteStr(ref bytes) => {
+                Ok(ast::LitKind::ByteStr(ref bytes)) => {
                     accumulator.extend_from_slice(&bytes);
                 }
                 _ => {
                     if !has_errors {
-                        invalid_type_err(cx, &e, false);
+                        invalid_type_err(cx, token_lit, e.span, false);
                     }
                     has_errors = true;
                 }
diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
index 345db700298..240167146e1 100644
--- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs
@@ -1,4 +1,3 @@
-use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 use crate::deriving::path_std;
 
@@ -19,7 +18,6 @@ pub fn expand_deriving_copy(
         path: path_std!(marker::Copy),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: true,
         methods: Vec::new(),
         associated_types: Vec::new(),
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 55cbb654723..2f19fbcac7d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -75,7 +75,6 @@ pub fn expand_deriving_clone(
         path: path_std!(clone::Clone),
         skip_path_as_bound: false,
         additional_bounds: bounds,
-        generics: Bounds::empty(),
         supports_unions: true,
         methods: vec![MethodDef {
             name: sym::clone,
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index 6190b7a8c77..a0b836171be 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -28,7 +28,6 @@ pub fn expand_deriving_eq(
         path: path_std!(cmp::Eq),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: true,
         methods: vec![MethodDef {
             name: sym::assert_receiver_is_total_eq,
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index d2412b20a09..52780981248 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -22,7 +22,6 @@ pub fn expand_deriving_ord(
         path: path_std!(cmp::Ord),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::cmp,
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index 353f28fc45f..34de4a620b4 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -86,7 +86,6 @@ pub fn expand_deriving_partial_eq(
         path: path_std!(cmp::PartialEq),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods,
         associated_types: Vec::new(),
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index e7458b23ef3..6cc8f26df55 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -40,7 +40,6 @@ pub fn expand_deriving_partial_ord(
         path: path_std!(cmp::PartialOrd),
         skip_path_as_bound: false,
         additional_bounds: vec![],
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![partial_cmp_def],
         associated_types: Vec::new(),
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index e6d5759bb52..544d971b27a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -23,7 +23,6 @@ pub fn expand_deriving_debug(
         path: path_std!(fmt::Debug),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::fmt,
diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
index 37aa665e5c6..6d14875a983 100644
--- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs
@@ -26,7 +26,6 @@ pub fn expand_deriving_rustc_decodable(
         path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::decode,
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 234957ab8a1..93f297ad88b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -27,7 +27,6 @@ pub fn expand_deriving_default(
         path: Path::new(vec![kw::Default, sym::Default]),
         skip_path_as_bound: has_a_default_variant(item),
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: kw::Default,
diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
index baacaa8b979..9a46ca81537 100644
--- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs
@@ -110,7 +110,6 @@ pub fn expand_deriving_rustc_encodable(
         path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global),
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::encode,
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 3972c3b4934..f48c49f411c 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -195,9 +195,6 @@ pub struct TraitDef<'a> {
     /// other than the current trait
     pub additional_bounds: Vec<Ty>,
 
-    /// Any extra lifetimes and/or bounds, e.g., `D: serialize::Decoder`
-    pub generics: Bounds,
-
     /// Can this trait be derived for unions?
     pub supports_unions: bool,
 
@@ -583,19 +580,21 @@ impl<'a> TraitDef<'a> {
             })
         });
 
-        let Generics { mut params, mut where_clause, .. } =
-            self.generics.to_generics(cx, self.span, type_ident, generics);
+        let mut where_clause = ast::WhereClause::default();
         where_clause.span = generics.where_clause.span;
         let ctxt = self.span.ctxt();
         let span = generics.span.with_ctxt(ctxt);
 
         // Create the generic parameters
-        params.extend(generics.params.iter().map(|param| match &param.kind {
-            GenericParamKind::Lifetime { .. } => param.clone(),
-            GenericParamKind::Type { .. } => {
-                // I don't think this can be moved out of the loop, since
-                // a GenericBound requires an ast id
-                let bounds: Vec<_> =
+        let params: Vec<_> = generics
+            .params
+            .iter()
+            .map(|param| match &param.kind {
+                GenericParamKind::Lifetime { .. } => param.clone(),
+                GenericParamKind::Type { .. } => {
+                    // I don't think this can be moved out of the loop, since
+                    // a GenericBound requires an ast id
+                    let bounds: Vec<_> =
                     // extra restrictions on the generics parameters to the
                     // type being derived upon
                     self.additional_bounds.iter().map(|p| {
@@ -608,21 +607,22 @@ impl<'a> TraitDef<'a> {
                         param.bounds.iter().cloned()
                     ).collect();
 
-                cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
-            }
-            GenericParamKind::Const { ty, kw_span, .. } => {
-                let const_nodefault_kind = GenericParamKind::Const {
-                    ty: ty.clone(),
-                    kw_span: kw_span.with_ctxt(ctxt),
-
-                    // We can't have default values inside impl block
-                    default: None,
-                };
-                let mut param_clone = param.clone();
-                param_clone.kind = const_nodefault_kind;
-                param_clone
-            }
-        }));
+                    cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
+                }
+                GenericParamKind::Const { ty, kw_span, .. } => {
+                    let const_nodefault_kind = GenericParamKind::Const {
+                        ty: ty.clone(),
+                        kw_span: kw_span.with_ctxt(ctxt),
+
+                        // We can't have default values inside impl block
+                        default: None,
+                    };
+                    let mut param_clone = param.clone();
+                    param_clone.kind = const_nodefault_kind;
+                    param_clone
+                }
+            })
+            .collect();
 
         // and similarly for where clauses
         where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| {
@@ -1062,18 +1062,15 @@ impl<'a> MethodDef<'a> {
                 trait_.create_struct_field_access_fields(cx, selflike_args, struct_def, true);
             mk_body(cx, selflike_fields)
         } else {
-            // Neither packed nor copy. Need to use ref patterns.
+            // Packed and not copy. Need to use ref patterns.
             let prefixes: Vec<_> =
                 (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect();
-            let addr_of = always_copy;
-            let selflike_fields =
-                trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, addr_of);
+            let selflike_fields = trait_.create_struct_pattern_fields(cx, struct_def, &prefixes);
             let mut body = mk_body(cx, selflike_fields);
 
             let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]);
-            let by_ref = ByRef::from(is_packed && !always_copy);
             let patterns =
-                trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, by_ref);
+                trait_.create_struct_patterns(cx, struct_path, struct_def, &prefixes, ByRef::Yes);
 
             // Do the let-destructuring.
             let mut stmts: Vec<_> = iter::zip(selflike_args, patterns)
@@ -1254,9 +1251,7 @@ impl<'a> MethodDef<'a> {
                 // A single arm has form (&VariantK, &VariantK, ...) => BodyK
                 // (see "Final wrinkle" note below for why.)
 
-                let addr_of = false; // because enums can't be repr(packed)
-                let fields =
-                    trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes, addr_of);
+                let fields = trait_.create_struct_pattern_fields(cx, &variant.data, &prefixes);
 
                 let sp = variant.span.with_ctxt(trait_.span.ctxt());
                 let variant_path = cx.path(sp, vec![type_ident, variant.ident]);
@@ -1519,15 +1514,13 @@ impl<'a> TraitDef<'a> {
         cx: &mut ExtCtxt<'_>,
         struct_def: &'a VariantData,
         prefixes: &[String],
-        addr_of: bool,
     ) -> Vec<FieldInfo> {
         self.create_fields(struct_def, |i, _struct_field, sp| {
             prefixes
                 .iter()
                 .map(|prefix| {
                     let ident = self.mk_pattern_ident(prefix, i);
-                    let expr = cx.expr_path(cx.path_ident(sp, ident));
-                    if addr_of { cx.expr_addr_of(sp, expr) } else { expr }
+                    cx.expr_path(cx.path_ident(sp, ident))
                 })
                 .collect()
         })
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 8fb1a4ba262..c136bb7141a 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -25,7 +25,6 @@ pub fn expand_deriving_hash(
         path,
         skip_path_as_bound: false,
         additional_bounds: Vec::new(),
-        generics: Bounds::empty(),
         supports_unions: false,
         methods: vec![MethodDef {
             name: sym::hash,
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 148b66d959e..5478caee57d 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -47,7 +47,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
         if let Err(err) = fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) {
             all_constants_ok = false;
             match err {
-                ErrorHandled::Reported(_) | ErrorHandled::Linted => {
+                ErrorHandled::Reported(_) => {
                     fx.tcx.sess.span_err(constant.span, "erroneous constant encountered");
                 }
                 ErrorHandled::TooGeneric => {
@@ -128,7 +128,7 @@ pub(crate) fn codegen_const_value<'tcx>(
     ty: Ty<'tcx>,
 ) -> CValue<'tcx> {
     let layout = fx.layout_of(ty);
-    assert!(!layout.is_unsized(), "sized const value");
+    assert!(layout.is_sized(), "unsized const value");
 
     if layout.is_zst() {
         return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout);
@@ -398,7 +398,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
         let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
         data_ctx.define(bytes.into_boxed_slice());
 
-        for &(offset, alloc_id) in alloc.provenance().iter() {
+        for &(offset, alloc_id) in alloc.provenance().ptrs().iter() {
             let addend = {
                 let endianness = tcx.data_layout.endian;
                 let offset = offset.bytes() as usize;
@@ -431,7 +431,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
                     {
                         tcx.sess.fatal(&format!(
                             "Allocation {:?} contains reference to TLS value {:?}",
-                            alloc, def_id
+                            alloc_id, def_id
                         ));
                     }
 
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index c3dfbd37279..c5bd574623d 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -19,7 +19,7 @@ fn codegen_field<'tcx>(
     };
 
     if let Some(extra) = extra {
-        if !field_layout.is_unsized() {
+        if field_layout.is_sized() {
             return simple(fx);
         }
         match field_layout.ty.kind() {
@@ -364,7 +364,7 @@ impl<'tcx> CPlace<'tcx> {
         fx: &mut FunctionCx<'_, '_, 'tcx>,
         layout: TyAndLayout<'tcx>,
     ) -> CPlace<'tcx> {
-        assert!(!layout.is_unsized());
+        assert!(layout.is_sized());
         if layout.size.bytes() == 0 {
             return CPlace {
                 inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None),
@@ -825,7 +825,7 @@ impl<'tcx> CPlace<'tcx> {
         fx: &FunctionCx<'_, '_, 'tcx>,
         variant: VariantIdx,
     ) -> Self {
-        assert!(!self.layout().is_unsized());
+        assert!(self.layout().is_sized());
         let layout = self.layout().for_variant(fx, variant);
         CPlace { inner: self.inner, layout }
     }
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index a314b7cc215..782f6856654 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -755,11 +755,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         OperandRef { val, layout: place.layout }
     }
 
-    fn write_operand_repeatedly(mut self, cg_elem: OperandRef<'tcx, RValue<'gcc>>, count: u64, dest: PlaceRef<'tcx, RValue<'gcc>>) -> Self {
+    fn write_operand_repeatedly(&mut self, cg_elem: OperandRef<'tcx, RValue<'gcc>>, count: u64, dest: PlaceRef<'tcx, RValue<'gcc>>) {
         let zero = self.const_usize(0);
         let count = self.const_usize(count);
-        let start = dest.project_index(&mut self, zero).llval;
-        let end = dest.project_index(&mut self, count).llval;
+        let start = dest.project_index(self, zero).llval;
+        let end = dest.project_index(self, count).llval;
 
         let header_bb = self.append_sibling_block("repeat_loop_header");
         let body_bb = self.append_sibling_block("repeat_loop_body");
@@ -778,14 +778,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
 
         self.switch_to_block(body_bb);
         let align = dest.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size);
-        cg_elem.val.store(&mut self, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align));
+        cg_elem.val.store(self, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align));
 
         let next = self.inbounds_gep(self.backend_type(cg_elem.layout), current.to_rvalue(), &[self.const_usize(1)]);
         self.llbb().add_assignment(None, current, next);
         self.br(header_bb);
 
         self.switch_to_block(next_bb);
-        self
     }
 
     fn range_metadata(&mut self, _load: RValue<'gcc>, _range: WrappingRange) {
diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs
index 81f53328867..111bfeb1322 100644
--- a/compiler/rustc_codegen_gcc/src/consts.rs
+++ b/compiler/rustc_codegen_gcc/src/consts.rs
@@ -297,12 +297,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
 
 pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: ConstAllocation<'tcx>) -> RValue<'gcc> {
     let alloc = alloc.inner();
-    let mut llvals = Vec::with_capacity(alloc.provenance().len() + 1);
+    let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1);
     let dl = cx.data_layout();
     let pointer_size = dl.pointer_size.bytes() as usize;
 
     let mut next_offset = 0;
-    for &(offset, alloc_id) in alloc.provenance().iter() {
+    for &(offset, alloc_id) in alloc.provenance().ptrs().iter() {
         let offset = offset.bytes();
         assert_eq!(offset as usize as u64, offset);
         let offset = offset as usize;
diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs
index 266759ed6cf..a81585d4128 100644
--- a/compiler/rustc_codegen_gcc/src/debuginfo.rs
+++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs
@@ -4,8 +4,9 @@ use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods};
 use rustc_middle::mir;
 use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
 use rustc_span::{SourceFile, Span, Symbol};
-use rustc_target::abi::Size;
 use rustc_target::abi::call::FnAbi;
+use rustc_target::abi::Size;
+use std::ops::Range;
 
 use crate::builder::Builder;
 use crate::context::CodegenCx;
@@ -13,7 +14,15 @@ use crate::context::CodegenCx;
 impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
     // FIXME(eddyb) find a common convention for all of the debuginfo-related
     // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
-    fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) {
+    fn dbg_var_addr(
+        &mut self,
+        _dbg_var: Self::DIVariable,
+        _scope_metadata: Self::DIScope,
+        _variable_alloca: Self::Value,
+        _direct_offset: Size,
+        _indirect_offsets: &[Size],
+        _fragment: Option<Range<Size>>,
+    ) {
         unimplemented!();
     }
 
@@ -31,16 +40,31 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
 }
 
 impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
-    fn create_vtable_debuginfo(&self, _ty: Ty<'tcx>, _trait_ref: Option<PolyExistentialTraitRef<'tcx>>, _vtable: Self::Value) {
+    fn create_vtable_debuginfo(
+        &self,
+        _ty: Ty<'tcx>,
+        _trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
+        _vtable: Self::Value,
+    ) {
         // TODO(antoyo)
     }
 
-    fn create_function_debug_context(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
+    fn create_function_debug_context(
+        &self,
+        _instance: Instance<'tcx>,
+        _fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+        _llfn: RValue<'gcc>,
+        _mir: &mir::Body<'tcx>,
+    ) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
         // TODO(antoyo)
         None
     }
 
-    fn extend_scope_to_file(&self, _scope_metadata: Self::DIScope, _file: &SourceFile) -> Self::DIScope {
+    fn extend_scope_to_file(
+        &self,
+        _scope_metadata: Self::DIScope,
+        _file: &SourceFile,
+    ) -> Self::DIScope {
         unimplemented!();
     }
 
@@ -48,15 +72,32 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         // TODO(antoyo)
     }
 
-    fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable {
+    fn create_dbg_var(
+        &self,
+        _variable_name: Symbol,
+        _variable_type: Ty<'tcx>,
+        _scope_metadata: Self::DIScope,
+        _variable_kind: VariableKind,
+        _span: Span,
+    ) -> Self::DIVariable {
         unimplemented!();
     }
 
-    fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option<RValue<'gcc>>) -> Self::DIScope {
+    fn dbg_scope_fn(
+        &self,
+        _instance: Instance<'tcx>,
+        _fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+        _maybe_definition_llfn: Option<RValue<'gcc>>,
+    ) -> Self::DIScope {
         unimplemented!();
     }
 
-    fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option<Self::DILocation>, _span: Span) -> Self::DILocation {
+    fn dbg_loc(
+        &self,
+        _scope: Self::DIScope,
+        _inlined_at: Option<Self::DILocation>,
+        _span: Span,
+    ) -> Self::DILocation {
         unimplemented!();
     }
 }
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index dd0daf2c38b..b9600da5c39 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -315,10 +315,10 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
                 false
             }
             /*
-               adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512gfni,
-               avx512ifma, avx512pf, avx512vaes, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpclmulqdq,
-               avx512vpopcntdq, bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
-               sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, xsave, xsavec, xsaveopt, xsaves
+               adx, aes, avx, avx2, avx512bf16, avx512bitalg, avx512bw, avx512cd, avx512dq, avx512er, avx512f, avx512ifma,
+               avx512pf, avx512vbmi, avx512vbmi2, avx512vl, avx512vnni, avx512vp2intersect, avx512vpopcntdq,
+               bmi1, bmi2, cmpxchg16b, ermsb, f16c, fma, fxsr, gfni, lzcnt, movbe, pclmulqdq, popcnt, rdrand, rdseed, rtm,
+               sha, sse, sse2, sse3, sse4.1, sse4.2, sse4a, ssse3, tbm, vaes, vpclmulqdq, xsave, xsavec, xsaveopt, xsaves
              */
             //false
         })
diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs
index 862ed62c68b..bdf7318ce48 100644
--- a/compiler/rustc_codegen_gcc/src/type_.rs
+++ b/compiler/rustc_codegen_gcc/src/type_.rs
@@ -277,7 +277,7 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
         offset = target_offset + field.size;
         prev_effective_align = effective_field_align;
     }
-    if !layout.is_unsized() && field_count > 0 {
+    if layout.is_sized() && field_count > 0 {
         if offset > layout.size {
             bug!("layout: {:#?} stride: {:?} offset: {:?}", layout, layout.size, offset);
         }
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 11053a8f6c4..97d0de47b3a 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -765,11 +765,21 @@ pub(crate) unsafe fn codegen(
         drop(handlers);
     }
 
+    // `.dwo` files are only emitted if:
+    //
+    // - Object files are being emitted (i.e. bitcode only or metadata only compilations will not
+    //   produce dwarf objects, even if otherwise enabled)
+    // - Target supports Split DWARF
+    // - Split debuginfo is enabled
+    // - Split DWARF kind is `split` (i.e. debuginfo is split into `.dwo` files, not different
+    //   sections in the `.o` files).
+    let dwarf_object_emitted = matches!(config.emit_obj, EmitObj::ObjectCode(_))
+        && cgcx.target_can_use_split_dwarf
+        && cgcx.split_debuginfo != SplitDebuginfo::Off
+        && cgcx.split_dwarf_kind == SplitDwarfKind::Split;
     Ok(module.into_compiled_module(
         config.emit_obj != EmitObj::None,
-        cgcx.target_can_use_split_dwarf
-            && cgcx.split_debuginfo != SplitDebuginfo::Off
-            && cgcx.split_dwarf_kind == SplitDwarfKind::Split,
+        dwarf_object_emitted,
         config.emit_bc,
         &cgcx.output_filenames,
     ))
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 9cb36ce7f18..77dd15ef4d8 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -556,15 +556,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
     }
 
     fn write_operand_repeatedly(
-        mut self,
+        &mut self,
         cg_elem: OperandRef<'tcx, &'ll Value>,
         count: u64,
         dest: PlaceRef<'tcx, &'ll Value>,
-    ) -> Self {
+    ) {
         let zero = self.const_usize(0);
         let count = self.const_usize(count);
-        let start = dest.project_index(&mut self, zero).llval;
-        let end = dest.project_index(&mut self, count).llval;
+        let start = dest.project_index(self, zero).llval;
+        let end = dest.project_index(self, count).llval;
 
         let header_bb = self.append_sibling_block("repeat_loop_header");
         let body_bb = self.append_sibling_block("repeat_loop_body");
@@ -592,7 +592,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         body_bx.br(header_bb);
         header_bx.add_incoming_to_phi(current, next, body_bb);
 
-        Self::build(self.cx, next_bb)
+        *self = Self::build(self.cx, next_bb);
     }
 
     fn range_metadata(&mut self, load: &'ll Value, range: WrappingRange) {
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 3b504d3a7df..69434280b21 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -28,7 +28,7 @@ use std::ops::Range;
 
 pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
     let alloc = alloc.inner();
-    let mut llvals = Vec::with_capacity(alloc.provenance().len() + 1);
+    let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1);
     let dl = cx.data_layout();
     let pointer_size = dl.pointer_size.bytes() as usize;
 
@@ -40,9 +40,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
         alloc: &'a Allocation,
         range: Range<usize>,
     ) {
-        let chunks = alloc
-            .init_mask()
-            .range_as_init_chunks(Size::from_bytes(range.start), Size::from_bytes(range.end));
+        let chunks = alloc.init_mask().range_as_init_chunks(range.clone().into());
 
         let chunk_to_llval = move |chunk| match chunk {
             InitChunk::Init(range) => {
@@ -80,7 +78,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
     }
 
     let mut next_offset = 0;
-    for &(offset, alloc_id) in alloc.provenance().iter() {
+    for &(offset, alloc_id) in alloc.provenance().ptrs().iter() {
         let offset = offset.bytes();
         assert_eq!(offset as usize as u64, offset);
         let offset = offset as usize;
@@ -489,7 +487,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
                     // happens to be zero. Instead, we should only check the value of defined bytes
                     // and set all undefined bytes to zero if this allocation is headed for the
                     // BSS.
-                    let all_bytes_are_zero = alloc.provenance().is_empty()
+                    let all_bytes_are_zero = alloc.provenance().ptrs().is_empty()
                         && alloc
                             .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())
                             .iter()
@@ -513,7 +511,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
                         section.as_str().as_ptr().cast(),
                         section.as_str().len() as c_uint,
                     );
-                    assert!(alloc.provenance().is_empty());
+                    assert!(alloc.provenance().ptrs().is_empty());
 
                     // The `inspect` method is okay here because we checked for provenance, and
                     // because we are doing this access to inspect the final interpreter state (not
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index b23fe3fc9d5..ca7a07d8391 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -39,6 +39,7 @@ use smallvec::SmallVec;
 use std::cell::OnceCell;
 use std::cell::RefCell;
 use std::iter;
+use std::ops::Range;
 
 mod create_scope_map;
 pub mod gdb;
@@ -163,12 +164,14 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
         variable_alloca: Self::Value,
         direct_offset: Size,
         indirect_offsets: &[Size],
+        fragment: Option<Range<Size>>,
     ) {
-        // Convert the direct and indirect offsets to address ops.
+        // Convert the direct and indirect offsets and fragment byte range to address ops.
         // FIXME(eddyb) use `const`s instead of getting the values via FFI,
         // the values should match the ones in the DWARF standard anyway.
         let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() };
         let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() };
+        let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() };
         let mut addr_ops = SmallVec::<[u64; 8]>::new();
 
         if direct_offset.bytes() > 0 {
@@ -182,6 +185,13 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
                 addr_ops.push(offset.bytes() as u64);
             }
         }
+        if let Some(fragment) = fragment {
+            // `DW_OP_LLVM_fragment` takes as arguments the fragment's
+            // offset and size, both of them in bits.
+            addr_ops.push(op_llvm_fragment());
+            addr_ops.push(fragment.start.bits() as u64);
+            addr_ops.push((fragment.end - fragment.start).bits() as u64);
+        }
 
         unsafe {
             // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
index a40cfc8b23f..5cd0e1cb63a 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
@@ -72,7 +72,7 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
         layout.is_unsized()
     );
 
-    if !layout.is_unsized() {
+    if layout.is_sized() {
         return None;
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index e2d0390821d..8f7728da9dd 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -2210,6 +2210,7 @@ extern "C" {
     ) -> &'a DILocation;
     pub fn LLVMRustDIBuilderCreateOpDeref() -> u64;
     pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64;
+    pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64;
 
     #[allow(improper_ctypes)]
     pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index e1f54356228..4af1aaec0a1 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -163,6 +163,9 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]
         ("x86", "rdrand") => smallvec!["rdrnd"],
         ("x86", "bmi1") => smallvec!["bmi"],
         ("x86", "cmpxchg16b") => smallvec!["cx16"],
+        // FIXME: These aliases are misleading, and should be removed before avx512_target_feature is
+        // stabilized. They must remain until std::arch switches off them.
+        // rust#100752
         ("x86", "avx512vaes") => smallvec!["vaes"],
         ("x86", "avx512gfni") => smallvec!["gfni"],
         ("x86", "avx512vpclmulqdq") => smallvec!["vpclmulqdq"],
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index dc1165835e7..182adf81785 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -140,7 +140,7 @@ fn struct_llfields<'a, 'tcx>(
         prev_effective_align = effective_field_align;
     }
     let padding_used = result.len() > field_count;
-    if !layout.is_unsized() && field_count > 0 {
+    if layout.is_sized() && field_count > 0 {
         if offset > layout.size {
             bug!("layout: {:#?} stride: {:?} offset: {:?}", layout, layout.size, offset);
         }
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index 18789d00fd3..2b1b06d1644 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -4,8 +4,11 @@ use rustc_session::cstore::DllImport;
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 
+use super::metadata::search_for_section;
+
 use object::read::archive::ArchiveFile;
 
+use std::error::Error;
 use std::fs::File;
 use std::io;
 use std::path::{Path, PathBuf};
@@ -56,6 +59,9 @@ pub trait ArchiveBuilderBuilder {
             if !bundled_lib_file_names.contains(&Symbol::intern(name)) {
                 continue; // We need to extract only native libraries.
             }
+            let data = search_for_section(rlib, data, ".bundled_lib").map_err(|e| {
+                ExtractBundledLibsError::ExtractSection { rlib, error: Box::<dyn Error>::from(e) }
+            })?;
             std::fs::write(&outdir.join(&name), data)
                 .map_err(|e| ExtractBundledLibsError::WriteFile { rlib, error: Box::new(e) })?;
         }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 2bd9023395d..2091730af22 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -8,7 +8,7 @@ use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_metadata::find_native_static_library;
-use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
+use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
 use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Lto, Strip};
@@ -29,7 +29,7 @@ use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
 use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
 use super::command::Command;
 use super::linker::{self, Linker};
-use super::metadata::{create_rmeta_file, MetadataPosition};
+use super::metadata::{create_wrapper_file, MetadataPosition};
 use super::rpath::{self, RPathConfig};
 use crate::{
     errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib,
@@ -44,7 +44,7 @@ use std::borrow::Borrow;
 use std::cell::OnceCell;
 use std::collections::BTreeSet;
 use std::ffi::OsString;
-use std::fs::{File, OpenOptions};
+use std::fs::{read, File, OpenOptions};
 use std::io::{BufWriter, Write};
 use std::ops::Deref;
 use std::path::{Path, PathBuf};
@@ -292,8 +292,8 @@ fn link_rlib<'a>(
     let trailing_metadata = match flavor {
         RlibFlavor::Normal => {
             let (metadata, metadata_position) =
-                create_rmeta_file(sess, codegen_results.metadata.raw_data());
-            let metadata = emit_metadata(sess, &metadata, tmpdir);
+                create_wrapper_file(sess, b".rmeta".to_vec(), codegen_results.metadata.raw_data());
+            let metadata = emit_wrapper_file(sess, &metadata, tmpdir, METADATA_FILENAME);
             match metadata_position {
                 MetadataPosition::First => {
                     // Most of the time metadata in rlib files is wrapped in a "dummy" object
@@ -376,12 +376,18 @@ fn link_rlib<'a>(
             let location =
                 find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
             if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal {
-                packed_bundled_libs.push(find_native_static_library(
-                    lib.filename.unwrap().as_str(),
+                let filename = lib.filename.unwrap();
+                let lib_path = find_native_static_library(
+                    filename.as_str(),
                     Some(true),
                     &lib_search_paths,
                     sess,
-                ));
+                );
+                let src = read(lib_path)
+                    .map_err(|e| sess.emit_fatal(errors::ReadFileError { message: e }))?;
+                let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src);
+                let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
+                packed_bundled_libs.push(wrapper_file);
                 continue;
             }
             ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| {
@@ -1582,6 +1588,9 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
 /// We only provide such support for a very limited number of targets.
 fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
     if let Some(self_contained) = sess.opts.cg.link_self_contained {
+        if sess.target.link_self_contained == LinkSelfContainedDefault::False {
+            sess.emit_err(errors::UnsupportedLinkSelfContained);
+        }
         return self_contained;
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index 99ddd176478..780a3850036 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -60,7 +60,7 @@ impl MetadataLoader for DefaultMetadataLoader {
                     let data = entry
                         .data(data)
                         .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
-                    return search_for_metadata(path, data, ".rmeta");
+                    return search_for_section(path, data, ".rmeta");
                 }
             }
 
@@ -69,11 +69,11 @@ impl MetadataLoader for DefaultMetadataLoader {
     }
 
     fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<MetadataRef, String> {
-        load_metadata_with(path, |data| search_for_metadata(path, data, ".rustc"))
+        load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
     }
 }
 
-fn search_for_metadata<'a>(
+pub(super) fn search_for_section<'a>(
     path: &Path,
     bytes: &'a [u8],
     section: &str,
@@ -223,7 +223,11 @@ pub enum MetadataPosition {
 // * ELF - All other targets are similar to Windows in that there's a
 //   `SHF_EXCLUDE` flag we can set on sections in an object file to get
 //   automatically removed from the final output.
-pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataPosition) {
+pub fn create_wrapper_file(
+    sess: &Session,
+    section_name: Vec<u8>,
+    data: &[u8],
+) -> (Vec<u8>, MetadataPosition) {
     let Some(mut file) = create_object_file(sess) else {
         // This is used to handle all "other" targets. This includes targets
         // in two categories:
@@ -241,11 +245,11 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataP
         // WebAssembly and for targets not supported by the `object` crate
         // yet it means that work will need to be done in the `object` crate
         // to add a case above.
-        return (metadata.to_vec(), MetadataPosition::Last);
+        return (data.to_vec(), MetadataPosition::Last);
     };
     let section = file.add_section(
         file.segment_name(StandardSegment::Debug).to_vec(),
-        b".rmeta".to_vec(),
+        section_name,
         SectionKind::Debug,
     );
     match file.format() {
@@ -259,7 +263,7 @@ pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> (Vec<u8>, MetadataP
         }
         _ => {}
     };
-    file.append_section_data(section, metadata, 1);
+    file.append_section_data(section, data, 1);
     (file.write().unwrap(), MetadataPosition::First)
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 36c94462b0b..ade50af0aee 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -507,6 +507,9 @@ pub enum ExtractBundledLibsError<'a> {
 
     #[diag(codegen_ssa_extract_bundled_libs_write_file)]
     WriteFile { rlib: &'a Path, error: Box<dyn std::error::Error> },
+
+    #[diag(codegen_ssa_extract_bundled_libs_write_file)]
+    ExtractSection { rlib: &'a Path, error: Box<dyn std::error::Error> },
 }
 
 #[derive(Diagnostic)]
@@ -521,3 +524,13 @@ pub enum AppleSdkRootError<'a> {
     #[diag(codegen_ssa_apple_sdk_error_sdk_path)]
     SdkPath { sdk_name: &'a str, error: Error },
 }
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_read_file)]
+pub struct ReadFileError {
+    pub message: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_unsupported_link_self_contained)]
+pub struct UnsupportedLinkSelfContained;
diff --git a/compiler/rustc_codegen_ssa/src/glue.rs b/compiler/rustc_codegen_ssa/src/glue.rs
index e6f402ef19d..6015d48deca 100644
--- a/compiler/rustc_codegen_ssa/src/glue.rs
+++ b/compiler/rustc_codegen_ssa/src/glue.rs
@@ -15,7 +15,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 ) -> (Bx::Value, Bx::Value) {
     let layout = bx.layout_of(t);
     debug!("size_and_align_of_dst(ty={}, info={:?}): layout: {:?}", t, info, layout);
-    if !layout.is_unsized() {
+    if layout.is_sized() {
         let size = bx.const_usize(layout.size.bytes());
         let align = bx.const_usize(layout.align.abi.bytes());
         return (size, align);
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index ceebe4d417f..ade33b6c777 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -1,12 +1,13 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
-#![feature(box_patterns)]
-#![feature(try_blocks)]
-#![feature(once_cell)]
 #![feature(associated_type_bounds)]
-#![feature(strict_provenance)]
-#![feature(int_roundings)]
+#![feature(box_patterns)]
 #![feature(if_let_guard)]
+#![feature(int_roundings)]
+#![feature(let_chains)]
 #![feature(never_type)]
+#![feature(once_cell)]
+#![feature(strict_provenance)]
+#![feature(try_blocks)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 0802067cde6..7822d924c01 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1,7 +1,7 @@
 use super::operand::OperandRef;
 use super::operand::OperandValue::{Immediate, Pair, Ref};
 use super::place::PlaceRef;
-use super::{FunctionCx, LocalRef};
+use super::{CachedLlbb, FunctionCx, LocalRef};
 
 use crate::base;
 use crate::common::{self, IntPredicate};
@@ -25,6 +25,15 @@ use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg};
 use rustc_target::abi::{self, HasDataLayout, WrappingRange};
 use rustc_target::spec::abi::Abi;
 
+// Indicates if we are in the middle of merging a BB's successor into it. This
+// can happen when BB jumps directly to its successor and the successor has no
+// other predecessors.
+#[derive(Debug, PartialEq)]
+enum MergingSucc {
+    False,
+    True,
+}
+
 /// Used by `FunctionCx::codegen_terminator` for emitting common patterns
 /// e.g., creating a basic block, calling a function, etc.
 struct TerminatorCodegenHelper<'tcx> {
@@ -64,31 +73,6 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
         }
     }
 
-    /// Get a basic block (creating it if necessary), possibly with a landing
-    /// pad next to it.
-    fn llbb_with_landing_pad<Bx: BuilderMethods<'a, 'tcx>>(
-        &self,
-        fx: &mut FunctionCx<'a, 'tcx, Bx>,
-        target: mir::BasicBlock,
-    ) -> (Bx::BasicBlock, bool) {
-        let span = self.terminator.source_info.span;
-        let lltarget = fx.llbb(target);
-        let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
-        match (self.funclet_bb, target_funclet) {
-            (None, None) => (lltarget, false),
-            // jump *into* cleanup - need a landing pad if GNU, cleanup pad if MSVC
-            (None, Some(_)) => (fx.landing_pad_for(target), false),
-            (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", self.terminator),
-            (Some(f), Some(t_f)) => {
-                if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) {
-                    (lltarget, false)
-                } else {
-                    (fx.landing_pad_for(target), true)
-                }
-            }
-        }
-    }
-
     /// Get a basic block (creating it if necessary), possibly with cleanup
     /// stuff in it or next to it.
     fn llbb_with_cleanup<Bx: BuilderMethods<'a, 'tcx>>(
@@ -96,7 +80,11 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
         fx: &mut FunctionCx<'a, 'tcx, Bx>,
         target: mir::BasicBlock,
     ) -> Bx::BasicBlock {
-        let (lltarget, is_cleanupret) = self.llbb_with_landing_pad(fx, target);
+        let (needs_landing_pad, is_cleanupret) = self.llbb_characteristics(fx, target);
+        let mut lltarget = fx.llbb(target);
+        if needs_landing_pad {
+            lltarget = fx.landing_pad_for(target);
+        }
         if is_cleanupret {
             // MSVC cross-funclet jump - need a trampoline
             debug_assert!(base::wants_msvc_seh(fx.cx.tcx().sess));
@@ -111,20 +99,54 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
         }
     }
 
+    fn llbb_characteristics<Bx: BuilderMethods<'a, 'tcx>>(
+        &self,
+        fx: &mut FunctionCx<'a, 'tcx, Bx>,
+        target: mir::BasicBlock,
+    ) -> (bool, bool) {
+        let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
+        let (needs_landing_pad, is_cleanupret) = match (self.funclet_bb, target_funclet) {
+            (None, None) => (false, false),
+            (None, Some(_)) => (true, false),
+            (Some(_), None) => {
+                let span = self.terminator.source_info.span;
+                span_bug!(span, "{:?} - jump out of cleanup?", self.terminator);
+            }
+            (Some(f), Some(t_f)) => {
+                if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) {
+                    (false, false)
+                } else {
+                    (true, true)
+                }
+            }
+        };
+        (needs_landing_pad, is_cleanupret)
+    }
+
     fn funclet_br<Bx: BuilderMethods<'a, 'tcx>>(
         &self,
         fx: &mut FunctionCx<'a, 'tcx, Bx>,
         bx: &mut Bx,
         target: mir::BasicBlock,
-    ) {
-        let (lltarget, is_cleanupret) = self.llbb_with_landing_pad(fx, target);
-        if is_cleanupret {
-            // MSVC micro-optimization: generate a `ret` rather than a jump
-            // to a trampoline.
-            debug_assert!(base::wants_msvc_seh(fx.cx.tcx().sess));
-            bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
+        mergeable_succ: bool,
+    ) -> MergingSucc {
+        let (needs_landing_pad, is_cleanupret) = self.llbb_characteristics(fx, target);
+        if mergeable_succ && !needs_landing_pad && !is_cleanupret {
+            // We can merge the successor into this bb, so no need for a `br`.
+            MergingSucc::True
         } else {
-            bx.br(lltarget);
+            let mut lltarget = fx.llbb(target);
+            if needs_landing_pad {
+                lltarget = fx.landing_pad_for(target);
+            }
+            if is_cleanupret {
+                // micro-optimization: generate a `ret` rather than a jump
+                // to a trampoline.
+                bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
+            } else {
+                bx.br(lltarget);
+            }
+            MergingSucc::False
         }
     }
 
@@ -140,7 +162,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
         destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
         cleanup: Option<mir::BasicBlock>,
         copied_constant_arguments: &[PlaceRef<'tcx, <Bx as BackendTypes>::Value>],
-    ) {
+        mergeable_succ: bool,
+    ) -> MergingSucc {
         // If there is a cleanup block and the function we're calling can unwind, then
         // do an invoke, otherwise do a call.
         let fn_ty = bx.fn_decl_backend_type(&fn_abi);
@@ -191,6 +214,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
                 }
                 fx.store_return(bx, ret_dest, &fn_abi.ret, invokeret);
             }
+            MergingSucc::False
         } else {
             let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &llargs, self.funclet(fx));
             if fx.mir[self.bb].is_cleanup {
@@ -206,9 +230,10 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
                     bx.lifetime_end(tmp.llval, tmp.layout.size);
                 }
                 fx.store_return(bx, ret_dest, &fn_abi.ret, llret);
-                self.funclet_br(fx, bx, target);
+                self.funclet_br(fx, bx, target, mergeable_succ)
             } else {
                 bx.unreachable();
+                MergingSucc::False
             }
         }
     }
@@ -225,7 +250,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
         destination: Option<mir::BasicBlock>,
         cleanup: Option<mir::BasicBlock>,
         instance: Instance<'_>,
-    ) {
+        mergeable_succ: bool,
+    ) -> MergingSucc {
         if let Some(cleanup) = cleanup {
             let ret_llbb = if let Some(target) = destination {
                 fx.llbb(target)
@@ -241,13 +267,15 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
                 instance,
                 Some((ret_llbb, self.llbb_with_cleanup(fx, cleanup), self.funclet(fx))),
             );
+            MergingSucc::False
         } else {
             bx.codegen_inline_asm(template, &operands, options, line_spans, instance, None);
 
             if let Some(target) = destination {
-                self.funclet_br(fx, bx, target);
+                self.funclet_br(fx, bx, target, mergeable_succ)
             } else {
                 bx.unreachable();
+                MergingSucc::False
             }
         }
     }
@@ -256,16 +284,16 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
 /// Codegen implementations for some terminator variants.
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     /// Generates code for a `Resume` terminator.
-    fn codegen_resume_terminator(&mut self, helper: TerminatorCodegenHelper<'tcx>, mut bx: Bx) {
+    fn codegen_resume_terminator(&mut self, helper: TerminatorCodegenHelper<'tcx>, bx: &mut Bx) {
         if let Some(funclet) = helper.funclet(self) {
             bx.cleanup_ret(funclet, None);
         } else {
-            let slot = self.get_personality_slot(&mut bx);
-            let lp0 = slot.project_field(&mut bx, 0);
+            let slot = self.get_personality_slot(bx);
+            let lp0 = slot.project_field(bx, 0);
             let lp0 = bx.load_operand(lp0).immediate();
-            let lp1 = slot.project_field(&mut bx, 1);
+            let lp1 = slot.project_field(bx, 1);
             let lp1 = bx.load_operand(lp1).immediate();
-            slot.storage_dead(&mut bx);
+            slot.storage_dead(bx);
 
             let mut lp = bx.const_undef(self.landing_pad_type());
             lp = bx.insert_value(lp, lp0, 0);
@@ -277,12 +305,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     fn codegen_switchint_terminator(
         &mut self,
         helper: TerminatorCodegenHelper<'tcx>,
-        mut bx: Bx,
+        bx: &mut Bx,
         discr: &mir::Operand<'tcx>,
         switch_ty: Ty<'tcx>,
         targets: &SwitchTargets,
     ) {
-        let discr = self.codegen_operand(&mut bx, &discr);
+        let discr = self.codegen_operand(bx, &discr);
         // `switch_ty` is redundant, sanity-check that.
         assert_eq!(discr.layout.ty, switch_ty);
         let mut target_iter = targets.iter();
@@ -338,7 +366,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         }
     }
 
-    fn codegen_return_terminator(&mut self, mut bx: Bx) {
+    fn codegen_return_terminator(&mut self, bx: &mut Bx) {
         // Call `va_end` if this is the definition of a C-variadic function.
         if self.fn_abi.c_variadic {
             // The `VaList` "spoofed" argument is just after all the real arguments.
@@ -368,11 +396,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
 
             PassMode::Direct(_) | PassMode::Pair(..) => {
-                let op = self.codegen_consume(&mut bx, mir::Place::return_place().as_ref());
+                let op = self.codegen_consume(bx, mir::Place::return_place().as_ref());
                 if let Ref(llval, _, align) = op.val {
                     bx.load(bx.backend_type(op.layout), llval, align)
                 } else {
-                    op.immediate_or_packed_pair(&mut bx)
+                    op.immediate_or_packed_pair(bx)
                 }
             }
 
@@ -388,8 +416,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 };
                 let llslot = match op.val {
                     Immediate(_) | Pair(..) => {
-                        let scratch = PlaceRef::alloca(&mut bx, self.fn_abi.ret.layout);
-                        op.val.store(&mut bx, scratch);
+                        let scratch = PlaceRef::alloca(bx, self.fn_abi.ret.layout);
+                        op.val.store(bx, scratch);
                         scratch.llval
                     }
                     Ref(llval, _, align) => {
@@ -409,22 +437,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     fn codegen_drop_terminator(
         &mut self,
         helper: TerminatorCodegenHelper<'tcx>,
-        mut bx: Bx,
+        bx: &mut Bx,
         location: mir::Place<'tcx>,
         target: mir::BasicBlock,
         unwind: Option<mir::BasicBlock>,
-    ) {
+        mergeable_succ: bool,
+    ) -> MergingSucc {
         let ty = location.ty(self.mir, bx.tcx()).ty;
         let ty = self.monomorphize(ty);
         let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty);
 
         if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
             // we don't actually need to drop anything.
-            helper.funclet_br(self, &mut bx, target);
-            return;
+            return helper.funclet_br(self, bx, target, mergeable_succ);
         }
 
-        let place = self.codegen_place(&mut bx, location.as_ref());
+        let place = self.codegen_place(bx, location.as_ref());
         let (args1, args2);
         let mut args = if let Some(llextra) = place.llextra {
             args2 = [place.llval, llextra];
@@ -462,7 +490,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 args = &args[..1];
                 (
                     meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE)
-                        .get_fn(&mut bx, vtable, ty, &fn_abi),
+                        .get_fn(bx, vtable, ty, &fn_abi),
                     fn_abi,
                 )
             }
@@ -507,7 +535,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 debug!("args' = {:?}", args);
                 (
                     meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE)
-                        .get_fn(&mut bx, vtable, ty, &fn_abi),
+                        .get_fn(bx, vtable, ty, &fn_abi),
                     fn_abi,
                 )
             }
@@ -515,29 +543,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         };
         helper.do_call(
             self,
-            &mut bx,
+            bx,
             fn_abi,
             drop_fn,
             args,
             Some((ReturnDest::Nothing, target)),
             unwind,
             &[],
-        );
+            mergeable_succ,
+        )
     }
 
     fn codegen_assert_terminator(
         &mut self,
         helper: TerminatorCodegenHelper<'tcx>,
-        mut bx: Bx,
+        bx: &mut Bx,
         terminator: &mir::Terminator<'tcx>,
         cond: &mir::Operand<'tcx>,
         expected: bool,
         msg: &mir::AssertMessage<'tcx>,
         target: mir::BasicBlock,
         cleanup: Option<mir::BasicBlock>,
-    ) {
+        mergeable_succ: bool,
+    ) -> MergingSucc {
         let span = terminator.source_info.span;
-        let cond = self.codegen_operand(&mut bx, cond).immediate();
+        let cond = self.codegen_operand(bx, cond).immediate();
         let mut const_cond = bx.const_to_opt_u128(cond, false).map(|c| c == 1);
 
         // This case can currently arise only from functions marked
@@ -555,8 +585,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         // Don't codegen the panic block if success if known.
         if const_cond == Some(expected) {
-            helper.funclet_br(self, &mut bx, target);
-            return;
+            return helper.funclet_br(self, bx, target, mergeable_succ);
         }
 
         // Pass the condition through llvm.expect for branch hinting.
@@ -573,16 +602,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         // After this point, bx is the block for the call to panic.
         bx.switch_to_block(panic_block);
-        self.set_debug_loc(&mut bx, terminator.source_info);
+        self.set_debug_loc(bx, terminator.source_info);
 
         // Get the location information.
-        let location = self.get_caller_location(&mut bx, terminator.source_info).immediate();
+        let location = self.get_caller_location(bx, terminator.source_info).immediate();
 
         // Put together the arguments to the panic entry point.
         let (lang_item, args) = match msg {
             AssertKind::BoundsCheck { ref len, ref index } => {
-                let len = self.codegen_operand(&mut bx, len).immediate();
-                let index = self.codegen_operand(&mut bx, index).immediate();
+                let len = self.codegen_operand(bx, len).immediate();
+                let index = self.codegen_operand(bx, index).immediate();
                 // It's `fn panic_bounds_check(index: usize, len: usize)`,
                 // and `#[track_caller]` adds an implicit third argument.
                 (LangItem::PanicBoundsCheck, vec![index, len, location])
@@ -595,29 +624,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
         };
 
-        let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), lang_item);
+        let (fn_abi, llfn) = common::build_langcall(bx, Some(span), lang_item);
 
         // Codegen the actual panic invoke/call.
-        helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup, &[]);
+        let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &args, None, cleanup, &[], false);
+        assert_eq!(merging_succ, MergingSucc::False);
+        MergingSucc::False
     }
 
     fn codegen_abort_terminator(
         &mut self,
         helper: TerminatorCodegenHelper<'tcx>,
-        mut bx: Bx,
+        bx: &mut Bx,
         terminator: &mir::Terminator<'tcx>,
     ) {
         let span = terminator.source_info.span;
-        self.set_debug_loc(&mut bx, terminator.source_info);
+        self.set_debug_loc(bx, terminator.source_info);
 
         // Obtain the panic entry point.
-        let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), LangItem::PanicNoUnwind);
+        let (fn_abi, llfn) = common::build_langcall(bx, Some(span), LangItem::PanicNoUnwind);
 
         // Codegen the actual panic invoke/call.
-        helper.do_call(self, &mut bx, fn_abi, llfn, &[], None, None, &[]);
+        let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &[], None, None, &[], false);
+        assert_eq!(merging_succ, MergingSucc::False);
     }
 
-    /// Returns `true` if this is indeed a panic intrinsic and codegen is done.
+    /// Returns `Some` if this is indeed a panic intrinsic and codegen is done.
     fn codegen_panic_intrinsic(
         &mut self,
         helper: &TerminatorCodegenHelper<'tcx>,
@@ -627,7 +659,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         source_info: mir::SourceInfo,
         target: Option<mir::BasicBlock>,
         cleanup: Option<mir::BasicBlock>,
-    ) -> bool {
+        mergeable_succ: bool,
+    ) -> Option<MergingSucc> {
         // Emit a panic or a no-op for `assert_*` intrinsics.
         // These are intrinsics that compile to panics so that we can get a message
         // which mentions the offending type, even from a const context.
@@ -653,7 +686,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 ZeroValid => !bx.tcx().permits_zero_init(layout),
                 UninitValid => !bx.tcx().permits_uninit_init(layout),
             };
-            if do_panic {
+            Some(if do_panic {
                 let msg_str = with_no_visible_paths!({
                     with_no_trimmed_paths!({
                         if layout.abi.is_uninhabited() {
@@ -686,22 +719,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)),
                     cleanup,
                     &[],
-                );
+                    mergeable_succ,
+                )
             } else {
                 // a NOP
                 let target = target.unwrap();
-                helper.funclet_br(self, bx, target)
-            }
-            true
+                helper.funclet_br(self, bx, target, mergeable_succ)
+            })
         } else {
-            false
+            None
         }
     }
 
     fn codegen_call_terminator(
         &mut self,
         helper: TerminatorCodegenHelper<'tcx>,
-        mut bx: Bx,
+        bx: &mut Bx,
         terminator: &mir::Terminator<'tcx>,
         func: &mir::Operand<'tcx>,
         args: &[mir::Operand<'tcx>],
@@ -709,12 +742,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         target: Option<mir::BasicBlock>,
         cleanup: Option<mir::BasicBlock>,
         fn_span: Span,
-    ) {
+        mergeable_succ: bool,
+    ) -> MergingSucc {
         let source_info = terminator.source_info;
         let span = source_info.span;
 
         // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
-        let callee = self.codegen_operand(&mut bx, func);
+        let callee = self.codegen_operand(bx, func);
 
         let (instance, mut llfn) = match *callee.layout.ty.kind() {
             ty::FnDef(def_id, substs) => (
@@ -734,8 +768,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         if let Some(ty::InstanceDef::DropGlue(_, None)) = def {
             // Empty drop glue; a no-op.
             let target = target.unwrap();
-            helper.funclet_br(self, &mut bx, target);
-            return;
+            return helper.funclet_br(self, bx, target, mergeable_succ);
         }
 
         // FIXME(eddyb) avoid computing this if possible, when `instance` is
@@ -762,9 +795,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         };
 
         if intrinsic == Some(sym::transmute) {
-            if let Some(target) = target {
-                self.codegen_transmute(&mut bx, &args[0], destination);
-                helper.funclet_br(self, &mut bx, target);
+            return if let Some(target) = target {
+                self.codegen_transmute(bx, &args[0], destination);
+                helper.funclet_br(self, bx, target, mergeable_succ)
             } else {
                 // If we are trying to transmute to an uninhabited type,
                 // it is likely there is no allotted destination. In fact,
@@ -774,20 +807,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 // it must be unreachable.
                 assert_eq!(fn_abi.ret.layout.abi, abi::Abi::Uninhabited);
                 bx.unreachable();
-            }
-            return;
+                MergingSucc::False
+            };
         }
 
-        if self.codegen_panic_intrinsic(
+        if let Some(merging_succ) = self.codegen_panic_intrinsic(
             &helper,
-            &mut bx,
+            bx,
             intrinsic,
             instance,
             source_info,
             target,
             cleanup,
+            mergeable_succ,
         ) {
-            return;
+            return merging_succ;
         }
 
         // The arguments we'll be passing. Plus one to account for outptr, if used.
@@ -797,23 +831,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // Prepare the return value destination
         let ret_dest = if target.is_some() {
             let is_intrinsic = intrinsic.is_some();
-            self.make_return_dest(&mut bx, destination, &fn_abi.ret, &mut llargs, is_intrinsic)
+            self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs, is_intrinsic)
         } else {
             ReturnDest::Nothing
         };
 
         if intrinsic == Some(sym::caller_location) {
-            if let Some(target) = target {
-                let location = self
-                    .get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info });
+            return if let Some(target) = target {
+                let location =
+                    self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
 
                 if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
-                    location.val.store(&mut bx, tmp);
+                    location.val.store(bx, tmp);
                 }
-                self.store_return(&mut bx, ret_dest, &fn_abi.ret, location.immediate());
-                helper.funclet_br(self, &mut bx, target);
-            }
-            return;
+                self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate());
+                helper.funclet_br(self, bx, target, mergeable_succ)
+            } else {
+                MergingSucc::False
+            };
         }
 
         match intrinsic {
@@ -857,12 +892,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             }
                         }
 
-                        self.codegen_operand(&mut bx, arg)
+                        self.codegen_operand(bx, arg)
                     })
                     .collect();
 
                 Self::codegen_intrinsic_call(
-                    &mut bx,
+                    bx,
                     *instance.as_ref().unwrap(),
                     &fn_abi,
                     &args,
@@ -871,16 +906,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 );
 
                 if let ReturnDest::IndirectOperand(dst, _) = ret_dest {
-                    self.store_return(&mut bx, ret_dest, &fn_abi.ret, dst.llval);
+                    self.store_return(bx, ret_dest, &fn_abi.ret, dst.llval);
                 }
 
-                if let Some(target) = target {
-                    helper.funclet_br(self, &mut bx, target);
+                return if let Some(target) = target {
+                    helper.funclet_br(self, bx, target, mergeable_succ)
                 } else {
                     bx.unreachable();
-                }
-
-                return;
+                    MergingSucc::False
+                };
             }
         }
 
@@ -894,7 +928,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         let mut copied_constant_arguments = vec![];
         'make_args: for (i, arg) in first_args.iter().enumerate() {
-            let mut op = self.codegen_operand(&mut bx, arg);
+            let mut op = self.codegen_operand(bx, arg);
 
             if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {
                 match op.val {
@@ -909,7 +943,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             && !op.layout.ty.is_region_ptr()
                         {
                             for i in 0..op.layout.fields.count() {
-                                let field = op.extract_field(&mut bx, i);
+                                let field = op.extract_field(bx, i);
                                 if !field.layout.is_zst() {
                                     // we found the one non-zero-sized field that is allowed
                                     // now find *its* non-zero-sized field, or stop if it's a
@@ -926,7 +960,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         // data pointer and vtable. Look up the method in the vtable, and pass
                         // the data pointer as the first argument
                         llfn = Some(meth::VirtualIndex::from_index(idx).get_fn(
-                            &mut bx,
+                            bx,
                             meta,
                             op.layout.ty,
                             &fn_abi,
@@ -937,7 +971,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     Ref(data_ptr, Some(meta), _) => {
                         // by-value dynamic dispatch
                         llfn = Some(meth::VirtualIndex::from_index(idx).get_fn(
-                            &mut bx,
+                            bx,
                             meta,
                             op.layout.ty,
                             &fn_abi,
@@ -954,11 +988,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         }
                         // FIXME(dyn-star): Make sure this is done on a &dyn* receiver
                         let place = op.deref(bx.cx());
-                        let data_ptr = place.project_field(&mut bx, 0);
-                        let meta_ptr = place.project_field(&mut bx, 1);
+                        let data_ptr = place.project_field(bx, 0);
+                        let meta_ptr = place.project_field(bx, 1);
                         let meta = bx.load_operand(meta_ptr);
                         llfn = Some(meth::VirtualIndex::from_index(idx).get_fn(
-                            &mut bx,
+                            bx,
                             meta.immediate(),
                             op.layout.ty,
                             &fn_abi,
@@ -977,24 +1011,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             match (arg, op.val) {
                 (&mir::Operand::Copy(_), Ref(_, None, _))
                 | (&mir::Operand::Constant(_), Ref(_, None, _)) => {
-                    let tmp = PlaceRef::alloca(&mut bx, op.layout);
+                    let tmp = PlaceRef::alloca(bx, op.layout);
                     bx.lifetime_start(tmp.llval, tmp.layout.size);
-                    op.val.store(&mut bx, tmp);
+                    op.val.store(bx, tmp);
                     op.val = Ref(tmp.llval, None, tmp.align);
                     copied_constant_arguments.push(tmp);
                 }
                 _ => {}
             }
 
-            self.codegen_argument(&mut bx, op, &mut llargs, &fn_abi.args[i]);
+            self.codegen_argument(bx, op, &mut llargs, &fn_abi.args[i]);
         }
         let num_untupled = untuple.map(|tup| {
-            self.codegen_arguments_untupled(
-                &mut bx,
-                tup,
-                &mut llargs,
-                &fn_abi.args[first_args.len()..],
-            )
+            self.codegen_arguments_untupled(bx, tup, &mut llargs, &fn_abi.args[first_args.len()..])
         });
 
         let needs_location =
@@ -1014,14 +1043,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 fn_abi,
             );
             let location =
-                self.get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info });
+                self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
             debug!(
                 "codegen_call_terminator({:?}): location={:?} (fn_span {:?})",
                 terminator, location, fn_span
             );
 
             let last_arg = fn_abi.args.last().unwrap();
-            self.codegen_argument(&mut bx, location, &mut llargs, last_arg);
+            self.codegen_argument(bx, location, &mut llargs, last_arg);
         }
 
         let (is_indirect_call, fn_ptr) = match (llfn, instance) {
@@ -1046,40 +1075,43 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             bx.cond_br(cond, bb_pass, bb_fail);
 
             bx.switch_to_block(bb_pass);
-            helper.do_call(
+            let merging_succ = helper.do_call(
                 self,
-                &mut bx,
+                bx,
                 fn_abi,
                 fn_ptr,
                 &llargs,
                 target.as_ref().map(|&target| (ret_dest, target)),
                 cleanup,
                 &copied_constant_arguments,
+                false,
             );
+            assert_eq!(merging_succ, MergingSucc::False);
 
             bx.switch_to_block(bb_fail);
             bx.abort();
             bx.unreachable();
 
-            return;
+            return MergingSucc::False;
         }
 
         helper.do_call(
             self,
-            &mut bx,
+            bx,
             fn_abi,
             fn_ptr,
             &llargs,
             target.as_ref().map(|&target| (ret_dest, target)),
             cleanup,
             &copied_constant_arguments,
-        );
+            mergeable_succ,
+        )
     }
 
     fn codegen_asm_terminator(
         &mut self,
         helper: TerminatorCodegenHelper<'tcx>,
-        mut bx: Bx,
+        bx: &mut Bx,
         terminator: &mir::Terminator<'tcx>,
         template: &[ast::InlineAsmTemplatePiece],
         operands: &[mir::InlineAsmOperand<'tcx>],
@@ -1088,24 +1120,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         destination: Option<mir::BasicBlock>,
         cleanup: Option<mir::BasicBlock>,
         instance: Instance<'_>,
-    ) {
+        mergeable_succ: bool,
+    ) -> MergingSucc {
         let span = terminator.source_info.span;
 
         let operands: Vec<_> = operands
             .iter()
             .map(|op| match *op {
                 mir::InlineAsmOperand::In { reg, ref value } => {
-                    let value = self.codegen_operand(&mut bx, value);
+                    let value = self.codegen_operand(bx, value);
                     InlineAsmOperandRef::In { reg, value }
                 }
                 mir::InlineAsmOperand::Out { reg, late, ref place } => {
-                    let place = place.map(|place| self.codegen_place(&mut bx, place.as_ref()));
+                    let place = place.map(|place| self.codegen_place(bx, place.as_ref()));
                     InlineAsmOperandRef::Out { reg, late, place }
                 }
                 mir::InlineAsmOperand::InOut { reg, late, ref in_value, ref out_place } => {
-                    let in_value = self.codegen_operand(&mut bx, in_value);
+                    let in_value = self.codegen_operand(bx, in_value);
                     let out_place =
-                        out_place.map(|out_place| self.codegen_place(&mut bx, out_place.as_ref()));
+                        out_place.map(|out_place| self.codegen_place(bx, out_place.as_ref()));
                     InlineAsmOperandRef::InOut { reg, late, in_value, out_place }
                 }
                 mir::InlineAsmOperand::Const { ref value } => {
@@ -1143,7 +1176,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         helper.do_inlineasm(
             self,
-            &mut bx,
+            bx,
             template,
             &operands,
             options,
@@ -1151,71 +1184,128 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             destination,
             cleanup,
             instance,
-        );
+            mergeable_succ,
+        )
     }
 }
 
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
-    pub fn codegen_block(&mut self, bb: mir::BasicBlock) {
-        let llbb = self.llbb(bb);
-        let mut bx = Bx::build(self.cx, llbb);
+    pub fn codegen_block(&mut self, mut bb: mir::BasicBlock) {
+        let llbb = match self.try_llbb(bb) {
+            Some(llbb) => llbb,
+            None => return,
+        };
+        let bx = &mut Bx::build(self.cx, llbb);
         let mir = self.mir;
-        let data = &mir[bb];
 
-        debug!("codegen_block({:?}={:?})", bb, data);
+        // MIR basic blocks stop at any function call. This may not be the case
+        // for the backend's basic blocks, in which case we might be able to
+        // combine multiple MIR basic blocks into a single backend basic block.
+        loop {
+            let data = &mir[bb];
 
-        for statement in &data.statements {
-            bx = self.codegen_statement(bx, statement);
-        }
+            debug!("codegen_block({:?}={:?})", bb, data);
+
+            for statement in &data.statements {
+                self.codegen_statement(bx, statement);
+            }
 
-        self.codegen_terminator(bx, bb, data.terminator());
+            let merging_succ = self.codegen_terminator(bx, bb, data.terminator());
+            if let MergingSucc::False = merging_succ {
+                break;
+            }
+
+            // We are merging the successor into the produced backend basic
+            // block. Record that the successor should be skipped when it is
+            // reached.
+            //
+            // Note: we must not have already generated code for the successor.
+            // This is implicitly ensured by the reverse postorder traversal,
+            // and the assertion explicitly guarantees that.
+            let mut successors = data.terminator().successors();
+            let succ = successors.next().unwrap();
+            assert!(matches!(self.cached_llbbs[succ], CachedLlbb::None));
+            self.cached_llbbs[succ] = CachedLlbb::Skip;
+            bb = succ;
+        }
     }
 
     fn codegen_terminator(
         &mut self,
-        mut bx: Bx,
+        bx: &mut Bx,
         bb: mir::BasicBlock,
         terminator: &'tcx mir::Terminator<'tcx>,
-    ) {
+    ) -> MergingSucc {
         debug!("codegen_terminator: {:?}", terminator);
 
         // Create the cleanup bundle, if needed.
         let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
         let helper = TerminatorCodegenHelper { bb, terminator, funclet_bb };
 
-        self.set_debug_loc(&mut bx, terminator.source_info);
+        let mergeable_succ = || {
+            // Note: any call to `switch_to_block` will invalidate a `true` value
+            // of `mergeable_succ`.
+            let mut successors = terminator.successors();
+            if let Some(succ) = successors.next()
+                && successors.next().is_none()
+                && let &[succ_pred] = self.mir.basic_blocks.predecessors()[succ].as_slice()
+            {
+                // bb has a single successor, and bb is its only predecessor. This
+                // makes it a candidate for merging.
+                assert_eq!(succ_pred, bb);
+                true
+            } else {
+                false
+            }
+        };
+
+        self.set_debug_loc(bx, terminator.source_info);
         match terminator.kind {
-            mir::TerminatorKind::Resume => self.codegen_resume_terminator(helper, bx),
+            mir::TerminatorKind::Resume => {
+                self.codegen_resume_terminator(helper, bx);
+                MergingSucc::False
+            }
 
             mir::TerminatorKind::Abort => {
                 self.codegen_abort_terminator(helper, bx, terminator);
+                MergingSucc::False
             }
 
             mir::TerminatorKind::Goto { target } => {
-                helper.funclet_br(self, &mut bx, target);
+                helper.funclet_br(self, bx, target, mergeable_succ())
             }
 
             mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref targets } => {
                 self.codegen_switchint_terminator(helper, bx, discr, switch_ty, targets);
+                MergingSucc::False
             }
 
             mir::TerminatorKind::Return => {
                 self.codegen_return_terminator(bx);
+                MergingSucc::False
             }
 
             mir::TerminatorKind::Unreachable => {
                 bx.unreachable();
+                MergingSucc::False
             }
 
             mir::TerminatorKind::Drop { place, target, unwind } => {
-                self.codegen_drop_terminator(helper, bx, place, target, unwind);
+                self.codegen_drop_terminator(helper, bx, place, target, unwind, mergeable_succ())
             }
 
-            mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => {
-                self.codegen_assert_terminator(
-                    helper, bx, terminator, cond, expected, msg, target, cleanup,
-                );
-            }
+            mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => self
+                .codegen_assert_terminator(
+                    helper,
+                    bx,
+                    terminator,
+                    cond,
+                    expected,
+                    msg,
+                    target,
+                    cleanup,
+                    mergeable_succ(),
+                ),
 
             mir::TerminatorKind::DropAndReplace { .. } => {
                 bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
@@ -1229,19 +1319,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 cleanup,
                 from_hir_call: _,
                 fn_span,
-            } => {
-                self.codegen_call_terminator(
-                    helper,
-                    bx,
-                    terminator,
-                    func,
-                    args,
-                    destination,
-                    target,
-                    cleanup,
-                    fn_span,
-                );
-            }
+            } => self.codegen_call_terminator(
+                helper,
+                bx,
+                terminator,
+                func,
+                args,
+                destination,
+                target,
+                cleanup,
+                fn_span,
+                mergeable_succ(),
+            ),
             mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Yield { .. } => {
                 bug!("generator ops in codegen")
             }
@@ -1256,20 +1345,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 line_spans,
                 destination,
                 cleanup,
-            } => {
-                self.codegen_asm_terminator(
-                    helper,
-                    bx,
-                    terminator,
-                    template,
-                    operands,
-                    options,
-                    line_spans,
-                    destination,
-                    cleanup,
-                    self.instance,
-                );
-            }
+            } => self.codegen_asm_terminator(
+                helper,
+                bx,
+                terminator,
+                template,
+                operands,
+                options,
+                line_spans,
+                destination,
+                cleanup,
+                self.instance,
+                mergeable_succ(),
+            ),
         }
     }
 
@@ -1587,12 +1675,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     // FIXME(eddyb) rename `llbb` and other `ll`-prefixed things to use a
     // more backend-agnostic prefix such as `cg` (i.e. this would be `cgbb`).
     pub fn llbb(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
-        self.cached_llbbs[bb].unwrap_or_else(|| {
-            // FIXME(eddyb) only name the block if `fewer_names` is `false`.
-            let llbb = Bx::append_block(self.cx, self.llfn, &format!("{:?}", bb));
-            self.cached_llbbs[bb] = Some(llbb);
-            llbb
-        })
+        self.try_llbb(bb).unwrap()
+    }
+
+    /// Like `llbb`, but may fail if the basic block should be skipped.
+    pub fn try_llbb(&mut self, bb: mir::BasicBlock) -> Option<Bx::BasicBlock> {
+        match self.cached_llbbs[bb] {
+            CachedLlbb::None => {
+                // FIXME(eddyb) only name the block if `fewer_names` is `false`.
+                let llbb = Bx::append_block(self.cx, self.llfn, &format!("{:?}", bb));
+                self.cached_llbbs[bb] = CachedLlbb::Some(llbb);
+                Some(llbb)
+            }
+            CachedLlbb::Some(llbb) => Some(llbb),
+            CachedLlbb::Skip => None,
+        }
     }
 
     fn make_return_dest(
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 157c1c82311..99283d3bb29 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -14,6 +14,8 @@ use super::operand::{OperandRef, OperandValue};
 use super::place::PlaceRef;
 use super::{FunctionCx, LocalRef};
 
+use std::ops::Range;
+
 pub struct FunctionDebugContext<S, L> {
     pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
 }
@@ -25,7 +27,7 @@ pub enum VariableKind {
 }
 
 /// Like `mir::VarDebugInfo`, but within a `mir::Local`.
-#[derive(Copy, Clone)]
+#[derive(Clone)]
 pub struct PerLocalVarDebugInfo<'tcx, D> {
     pub name: Symbol,
     pub source_info: mir::SourceInfo,
@@ -33,6 +35,10 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
     /// `DIVariable` returned by `create_dbg_var`.
     pub dbg_var: Option<D>,
 
+    /// Byte range in the `dbg_var` covered by this fragment,
+    /// if this is a fragment of a composite `VarDebugInfo`.
+    pub fragment: Option<Range<Size>>,
+
     /// `.place.projection` from `mir::VarDebugInfo`.
     pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
 }
@@ -145,7 +151,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             Some(per_local) => &per_local[local],
             None => return,
         };
-        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
+        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
         let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
 
         let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
@@ -187,6 +193,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     name,
                     source_info: decl.source_info,
                     dbg_var,
+                    fragment: None,
                     projection: ty::List::empty(),
                 })
             }
@@ -199,7 +206,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         let name = if bx.sess().fewer_names() {
             None
         } else {
-            Some(match whole_local_var.or(fallback_var) {
+            Some(match whole_local_var.or(fallback_var.clone()) {
                 Some(var) if var.name != kw::Empty => var.name.to_string(),
                 _ => format!("{:?}", local),
             })
@@ -249,7 +256,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             LocalRef::UnsizedPlace(_) => return,
         };
 
-        let vars = vars.iter().copied().chain(fallback_var);
+        let vars = vars.iter().cloned().chain(fallback_var);
 
         for var in vars {
             let Some(dbg_var) = var.dbg_var else { continue };
@@ -312,9 +319,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 bx.store(place.llval, alloca.llval, alloca.align);
 
                 // Point the debug info to `*alloca` for the current variable
-                bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]);
+                bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
             } else {
-                bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
+                bx.dbg_var_addr(
+                    dbg_var,
+                    dbg_loc,
+                    base.llval,
+                    direct_offset,
+                    &indirect_offsets,
+                    None,
+                );
             }
         }
     }
@@ -382,6 +396,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         let ty = self.monomorphize(c.ty());
                         (ty, VariableKind::LocalVariable)
                     }
+                    mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
+                        let ty = self.monomorphize(ty);
+                        (ty, VariableKind::LocalVariable)
+                    }
                 };
 
                 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
@@ -393,6 +411,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         name: var.name,
                         source_info: var.source_info,
                         dbg_var,
+                        fragment: None,
                         projection: place.projection,
                     });
                 }
@@ -407,10 +426,48 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                 bx,
                             );
 
-                            bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]);
+                            bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
                         }
                     }
                 }
+                mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
+                    let var_ty = self.monomorphize(ty);
+                    let var_layout = self.cx.layout_of(var_ty);
+                    for fragment in fragments {
+                        let mut fragment_start = Size::ZERO;
+                        let mut fragment_layout = var_layout;
+
+                        for elem in &fragment.projection {
+                            match *elem {
+                                mir::ProjectionElem::Field(field, _) => {
+                                    let i = field.index();
+                                    fragment_start += fragment_layout.fields.offset(i);
+                                    fragment_layout = fragment_layout.field(self.cx, i);
+                                }
+                                _ => span_bug!(
+                                    var.source_info.span,
+                                    "unsupported fragment projection `{:?}`",
+                                    elem,
+                                ),
+                            }
+                        }
+
+                        let place = fragment.contents;
+                        per_local[place.local].push(PerLocalVarDebugInfo {
+                            name: var.name,
+                            source_info: var.source_info,
+                            dbg_var,
+                            fragment: if fragment_layout.size == var_layout.size {
+                                // Fragment covers entire variable, so as far as
+                                // DWARF is concerned, it's not really a fragment.
+                                None
+                            } else {
+                                Some(fragment_start..fragment_start + fragment_layout.size)
+                            },
+                            projection: place.projection,
+                        });
+                    }
+                }
             }
         }
         Some(per_local)
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index da9aaf00ecf..79c66a955e7 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -16,6 +16,18 @@ use rustc_middle::mir::traversal;
 
 use self::operand::{OperandRef, OperandValue};
 
+// Used for tracking the state of generated basic blocks.
+enum CachedLlbb<T> {
+    /// Nothing created yet.
+    None,
+
+    /// Has been created.
+    Some(T),
+
+    /// Nothing created yet, and nothing should be.
+    Skip,
+}
+
 /// Master context for codegenning from MIR.
 pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     instance: Instance<'tcx>,
@@ -43,7 +55,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     /// as-needed (e.g. RPO reaching it or another block branching to it).
     // FIXME(eddyb) rename `llbbs` and other `ll`-prefixed things to use a
     // more backend-agnostic prefix such as `cg` (i.e. this would be `cgbbs`).
-    cached_llbbs: IndexVec<mir::BasicBlock, Option<Bx::BasicBlock>>,
+    cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>>,
 
     /// The funclet status of each basic block
     cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>,
@@ -155,11 +167,13 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     }
 
     let cleanup_kinds = analyze::cleanup_kinds(&mir);
-    let cached_llbbs: IndexVec<mir::BasicBlock, Option<Bx::BasicBlock>> = mir
-        .basic_blocks
-        .indices()
-        .map(|bb| if bb == mir::START_BLOCK { Some(start_llbb) } else { None })
-        .collect();
+    let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
+        mir.basic_blocks
+            .indices()
+            .map(|bb| {
+                if bb == mir::START_BLOCK { CachedLlbb::Some(start_llbb) } else { CachedLlbb::None }
+            })
+            .collect();
 
     let mut fx = FunctionCx {
         instance,
@@ -189,7 +203,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
             all_consts_ok = false;
             match err {
                 // errored or at least linted
-                ErrorHandled::Reported(_) | ErrorHandled::Linted => {}
+                ErrorHandled::Reported(_) => {}
                 ErrorHandled::TooGeneric => {
                     span_bug!(const_.span, "codegen encountered polymorphic constant: {:?}", err)
                 }
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 8c72d6d1fbe..90855538589 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -29,7 +29,7 @@ pub struct PlaceRef<'tcx, V> {
 
 impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
     pub fn new_sized(llval: V, layout: TyAndLayout<'tcx>) -> PlaceRef<'tcx, V> {
-        assert!(!layout.is_unsized());
+        assert!(layout.is_sized());
         PlaceRef { llval, llextra: None, layout, align: layout.align.abi }
     }
 
@@ -38,7 +38,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         layout: TyAndLayout<'tcx>,
         align: Align,
     ) -> PlaceRef<'tcx, V> {
-        assert!(!layout.is_unsized());
+        assert!(layout.is_sized());
         PlaceRef { llval, llextra: None, layout, align }
     }
 
@@ -48,7 +48,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         bx: &mut Bx,
         layout: TyAndLayout<'tcx>,
     ) -> Self {
-        assert!(!layout.is_unsized(), "tried to statically allocate unsized place");
+        assert!(layout.is_sized(), "tried to statically allocate unsized place");
         let tmp = bx.alloca(bx.cx().backend_type(layout), layout.align.abi);
         Self::new_sized(tmp, layout)
     }
@@ -145,7 +145,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
                 );
                 return simple();
             }
-            _ if !field.is_unsized() => return simple(),
+            _ if field.is_sized() => return simple(),
             ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(),
             ty::Adt(def, _) => {
                 if def.repr().packed() {
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 4aab31fbfe7..9ad96f7a447 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -18,17 +18,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     #[instrument(level = "trace", skip(self, bx))]
     pub fn codegen_rvalue(
         &mut self,
-        mut bx: Bx,
+        bx: &mut Bx,
         dest: PlaceRef<'tcx, Bx::Value>,
         rvalue: &mir::Rvalue<'tcx>,
-    ) -> Bx {
+    ) {
         match *rvalue {
             mir::Rvalue::Use(ref operand) => {
-                let cg_operand = self.codegen_operand(&mut bx, operand);
+                let cg_operand = self.codegen_operand(bx, operand);
                 // FIXME: consider not copying constants through stack. (Fixable by codegen'ing
                 // constants into `OperandValue::Ref`; why don’t we do that yet if we don’t?)
-                cg_operand.val.store(&mut bx, dest);
-                bx
+                cg_operand.val.store(bx, dest);
             }
 
             mir::Rvalue::Cast(mir::CastKind::Pointer(PointerCast::Unsize), ref source, _) => {
@@ -37,16 +36,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 if bx.cx().is_backend_scalar_pair(dest.layout) {
                     // Into-coerce of a thin pointer to a fat pointer -- just
                     // use the operand path.
-                    let (mut bx, temp) = self.codegen_rvalue_operand(bx, rvalue);
-                    temp.val.store(&mut bx, dest);
-                    return bx;
+                    let temp = self.codegen_rvalue_operand(bx, rvalue);
+                    temp.val.store(bx, dest);
+                    return;
                 }
 
                 // Unsize of a nontrivial struct. I would prefer for
                 // this to be eliminated by MIR building, but
                 // `CoerceUnsized` can be passed by a where-clause,
                 // so the (generic) MIR may not be able to expand it.
-                let operand = self.codegen_operand(&mut bx, source);
+                let operand = self.codegen_operand(bx, source);
                 match operand.val {
                     OperandValue::Pair(..) | OperandValue::Immediate(_) => {
                         // Unsize from an immediate structure. We don't
@@ -56,63 +55,62 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         // index into the struct, and this case isn't
                         // important enough for it.
                         debug!("codegen_rvalue: creating ugly alloca");
-                        let scratch = PlaceRef::alloca(&mut bx, operand.layout);
-                        scratch.storage_live(&mut bx);
-                        operand.val.store(&mut bx, scratch);
-                        base::coerce_unsized_into(&mut bx, scratch, dest);
-                        scratch.storage_dead(&mut bx);
+                        let scratch = PlaceRef::alloca(bx, operand.layout);
+                        scratch.storage_live(bx);
+                        operand.val.store(bx, scratch);
+                        base::coerce_unsized_into(bx, scratch, dest);
+                        scratch.storage_dead(bx);
                     }
                     OperandValue::Ref(llref, None, align) => {
                         let source = PlaceRef::new_sized_aligned(llref, operand.layout, align);
-                        base::coerce_unsized_into(&mut bx, source, dest);
+                        base::coerce_unsized_into(bx, source, dest);
                     }
                     OperandValue::Ref(_, Some(_), _) => {
                         bug!("unsized coercion on an unsized rvalue");
                     }
                 }
-                bx
             }
 
             mir::Rvalue::Repeat(ref elem, count) => {
-                let cg_elem = self.codegen_operand(&mut bx, elem);
+                let cg_elem = self.codegen_operand(bx, elem);
 
                 // Do not generate the loop for zero-sized elements or empty arrays.
                 if dest.layout.is_zst() {
-                    return bx;
+                    return;
                 }
 
                 if let OperandValue::Immediate(v) = cg_elem.val {
                     let zero = bx.const_usize(0);
-                    let start = dest.project_index(&mut bx, zero).llval;
+                    let start = dest.project_index(bx, zero).llval;
                     let size = bx.const_usize(dest.layout.size.bytes());
 
                     // Use llvm.memset.p0i8.* to initialize all zero arrays
                     if bx.cx().const_to_opt_u128(v, false) == Some(0) {
                         let fill = bx.cx().const_u8(0);
                         bx.memset(start, fill, size, dest.align, MemFlags::empty());
-                        return bx;
+                        return;
                     }
 
                     // Use llvm.memset.p0i8.* to initialize byte arrays
                     let v = bx.from_immediate(v);
                     if bx.cx().val_ty(v) == bx.cx().type_i8() {
                         bx.memset(start, v, size, dest.align, MemFlags::empty());
-                        return bx;
+                        return;
                     }
                 }
 
                 let count =
                     self.monomorphize(count).eval_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all());
 
-                bx.write_operand_repeatedly(cg_elem, count, dest)
+                bx.write_operand_repeatedly(cg_elem, count, dest);
             }
 
             mir::Rvalue::Aggregate(ref kind, ref operands) => {
                 let (dest, active_field_index) = match **kind {
                     mir::AggregateKind::Adt(adt_did, variant_index, _, _, active_field_index) => {
-                        dest.codegen_set_discr(&mut bx, variant_index);
+                        dest.codegen_set_discr(bx, variant_index);
                         if bx.tcx().adt_def(adt_did).is_enum() {
-                            (dest.project_downcast(&mut bx, variant_index), active_field_index)
+                            (dest.project_downcast(bx, variant_index), active_field_index)
                         } else {
                             (dest, active_field_index)
                         }
@@ -120,37 +118,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     _ => (dest, None),
                 };
                 for (i, operand) in operands.iter().enumerate() {
-                    let op = self.codegen_operand(&mut bx, operand);
+                    let op = self.codegen_operand(bx, operand);
                     // Do not generate stores and GEPis for zero-sized fields.
                     if !op.layout.is_zst() {
                         let field_index = active_field_index.unwrap_or(i);
                         let field = if let mir::AggregateKind::Array(_) = **kind {
                             let llindex = bx.cx().const_usize(field_index as u64);
-                            dest.project_index(&mut bx, llindex)
+                            dest.project_index(bx, llindex)
                         } else {
-                            dest.project_field(&mut bx, field_index)
+                            dest.project_field(bx, field_index)
                         };
-                        op.val.store(&mut bx, field);
+                        op.val.store(bx, field);
                     }
                 }
-                bx
             }
 
             _ => {
                 assert!(self.rvalue_creates_operand(rvalue, DUMMY_SP));
-                let (mut bx, temp) = self.codegen_rvalue_operand(bx, rvalue);
-                temp.val.store(&mut bx, dest);
-                bx
+                let temp = self.codegen_rvalue_operand(bx, rvalue);
+                temp.val.store(bx, dest);
             }
         }
     }
 
     pub fn codegen_rvalue_unsized(
         &mut self,
-        mut bx: Bx,
+        bx: &mut Bx,
         indirect_dest: PlaceRef<'tcx, Bx::Value>,
         rvalue: &mir::Rvalue<'tcx>,
-    ) -> Bx {
+    ) {
         debug!(
             "codegen_rvalue_unsized(indirect_dest.llval={:?}, rvalue={:?})",
             indirect_dest.llval, rvalue
@@ -158,9 +154,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         match *rvalue {
             mir::Rvalue::Use(ref operand) => {
-                let cg_operand = self.codegen_operand(&mut bx, operand);
-                cg_operand.val.store_unsized(&mut bx, indirect_dest);
-                bx
+                let cg_operand = self.codegen_operand(bx, operand);
+                cg_operand.val.store_unsized(bx, indirect_dest);
             }
 
             _ => bug!("unsized assignment other than `Rvalue::Use`"),
@@ -169,9 +164,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
     pub fn codegen_rvalue_operand(
         &mut self,
-        mut bx: Bx,
+        bx: &mut Bx,
         rvalue: &mir::Rvalue<'tcx>,
-    ) -> (Bx, OperandRef<'tcx, Bx::Value>) {
+    ) -> OperandRef<'tcx, Bx::Value> {
         assert!(
             self.rvalue_creates_operand(rvalue, DUMMY_SP),
             "cannot codegen {:?} to operand",
@@ -180,7 +175,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         match *rvalue {
             mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => {
-                let operand = self.codegen_operand(&mut bx, source);
+                let operand = self.codegen_operand(bx, source);
                 debug!("cast operand is {:?}", operand);
                 let cast = bx.cx().layout_of(self.monomorphize(mir_cast_ty));
 
@@ -245,7 +240,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             }
                         };
                         let (lldata, llextra) =
-                            base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra);
+                            base::unsize_ptr(bx, lldata, operand.layout.ty, cast.ty, llextra);
                         OperandValue::Pair(lldata, llextra)
                     }
                     mir::CastKind::Pointer(PointerCast::MutToConstPointer)
@@ -278,7 +273,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             OperandValue::Pair(v, l) => (v, Some(l)),
                         };
                         let (lldata, llextra) =
-                            base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra);
+                            base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra);
                         OperandValue::Pair(lldata, llextra)
                     }
                     mir::CastKind::Pointer(
@@ -299,7 +294,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         let ll_t_out = bx.cx().immediate_backend_type(cast);
                         if operand.layout.abi.is_uninhabited() {
                             let val = OperandValue::Immediate(bx.cx().const_undef(ll_t_out));
-                            return (bx, OperandRef { val, layout: cast });
+                            return OperandRef { val, layout: cast };
                         }
                         let r_t_in =
                             CastTy::from_ty(operand.layout.ty).expect("bad input type for cast");
@@ -348,7 +343,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         OperandValue::Immediate(newval)
                     }
                 };
-                (bx, OperandRef { val, layout: cast })
+                OperandRef { val, layout: cast }
             }
 
             mir::Rvalue::Ref(_, bk, place) => {
@@ -361,10 +356,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 self.codegen_place_to_pointer(bx, place, mk_ref)
             }
 
-            mir::Rvalue::CopyForDeref(place) => {
-                let operand = self.codegen_operand(&mut bx, &Operand::Copy(place));
-                (bx, operand)
-            }
+            mir::Rvalue::CopyForDeref(place) => self.codegen_operand(bx, &Operand::Copy(place)),
             mir::Rvalue::AddressOf(mutability, place) => {
                 let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
                     tcx.mk_ptr(ty::TypeAndMut { ty, mutbl: mutability })
@@ -373,23 +365,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
 
             mir::Rvalue::Len(place) => {
-                let size = self.evaluate_array_len(&mut bx, place);
-                let operand = OperandRef {
+                let size = self.evaluate_array_len(bx, place);
+                OperandRef {
                     val: OperandValue::Immediate(size),
                     layout: bx.cx().layout_of(bx.tcx().types.usize),
-                };
-                (bx, operand)
+                }
             }
 
             mir::Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => {
-                let lhs = self.codegen_operand(&mut bx, lhs);
-                let rhs = self.codegen_operand(&mut bx, rhs);
+                let lhs = self.codegen_operand(bx, lhs);
+                let rhs = self.codegen_operand(bx, rhs);
                 let llresult = match (lhs.val, rhs.val) {
                     (
                         OperandValue::Pair(lhs_addr, lhs_extra),
                         OperandValue::Pair(rhs_addr, rhs_extra),
                     ) => self.codegen_fat_ptr_binop(
-                        &mut bx,
+                        bx,
                         op,
                         lhs_addr,
                         lhs_extra,
@@ -399,22 +390,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     ),
 
                     (OperandValue::Immediate(lhs_val), OperandValue::Immediate(rhs_val)) => {
-                        self.codegen_scalar_binop(&mut bx, op, lhs_val, rhs_val, lhs.layout.ty)
+                        self.codegen_scalar_binop(bx, op, lhs_val, rhs_val, lhs.layout.ty)
                     }
 
                     _ => bug!(),
                 };
-                let operand = OperandRef {
+                OperandRef {
                     val: OperandValue::Immediate(llresult),
                     layout: bx.cx().layout_of(op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)),
-                };
-                (bx, operand)
+                }
             }
             mir::Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => {
-                let lhs = self.codegen_operand(&mut bx, lhs);
-                let rhs = self.codegen_operand(&mut bx, rhs);
+                let lhs = self.codegen_operand(bx, lhs);
+                let rhs = self.codegen_operand(bx, rhs);
                 let result = self.codegen_scalar_checked_binop(
-                    &mut bx,
+                    bx,
                     op,
                     lhs.immediate(),
                     rhs.immediate(),
@@ -422,13 +412,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 );
                 let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty);
                 let operand_ty = bx.tcx().intern_tup(&[val_ty, bx.tcx().types.bool]);
-                let operand = OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) };
-
-                (bx, operand)
+                OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) }
             }
 
             mir::Rvalue::UnaryOp(op, ref operand) => {
-                let operand = self.codegen_operand(&mut bx, operand);
+                let operand = self.codegen_operand(bx, operand);
                 let lloperand = operand.immediate();
                 let is_float = operand.layout.ty.is_floating_point();
                 let llval = match op {
@@ -441,22 +429,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         }
                     }
                 };
-                (bx, OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout })
+                OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout }
             }
 
             mir::Rvalue::Discriminant(ref place) => {
                 let discr_ty = rvalue.ty(self.mir, bx.tcx());
                 let discr_ty = self.monomorphize(discr_ty);
-                let discr = self
-                    .codegen_place(&mut bx, place.as_ref())
-                    .codegen_get_discr(&mut bx, discr_ty);
-                (
-                    bx,
-                    OperandRef {
-                        val: OperandValue::Immediate(discr),
-                        layout: self.cx.layout_of(discr_ty),
-                    },
-                )
+                let discr = self.codegen_place(bx, place.as_ref()).codegen_get_discr(bx, discr_ty);
+                OperandRef {
+                    val: OperandValue::Immediate(discr),
+                    layout: self.cx.layout_of(discr_ty),
+                }
             }
 
             mir::Rvalue::NullaryOp(null_op, ty) => {
@@ -469,36 +452,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 };
                 let val = bx.cx().const_usize(val);
                 let tcx = self.cx.tcx();
-                (
-                    bx,
-                    OperandRef {
-                        val: OperandValue::Immediate(val),
-                        layout: self.cx.layout_of(tcx.types.usize),
-                    },
-                )
+                OperandRef {
+                    val: OperandValue::Immediate(val),
+                    layout: self.cx.layout_of(tcx.types.usize),
+                }
             }
 
             mir::Rvalue::ThreadLocalRef(def_id) => {
                 assert!(bx.cx().tcx().is_static(def_id));
                 let static_ = bx.get_static(def_id);
                 let layout = bx.layout_of(bx.cx().tcx().static_ptr_ty(def_id));
-                let operand = OperandRef::from_immediate_or_packed_pair(&mut bx, static_, layout);
-                (bx, operand)
-            }
-            mir::Rvalue::Use(ref operand) => {
-                let operand = self.codegen_operand(&mut bx, operand);
-                (bx, operand)
+                OperandRef::from_immediate_or_packed_pair(bx, static_, layout)
             }
+            mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
             mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => {
                 // According to `rvalue_creates_operand`, only ZST
                 // aggregate rvalues are allowed to be operands.
                 let ty = rvalue.ty(self.mir, self.cx.tcx());
-                let operand =
-                    OperandRef::new_zst(&mut bx, self.cx.layout_of(self.monomorphize(ty)));
-                (bx, operand)
+                OperandRef::new_zst(bx, self.cx.layout_of(self.monomorphize(ty)))
             }
             mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
-                let operand = self.codegen_operand(&mut bx, operand);
+                let operand = self.codegen_operand(bx, operand);
                 let lloperand = operand.immediate();
 
                 let content_ty = self.monomorphize(content_ty);
@@ -506,8 +480,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let llty_ptr = bx.cx().backend_type(box_layout);
 
                 let val = bx.pointercast(lloperand, llty_ptr);
-                let operand = OperandRef { val: OperandValue::Immediate(val), layout: box_layout };
-                (bx, operand)
+                OperandRef { val: OperandValue::Immediate(val), layout: box_layout }
             }
         }
     }
@@ -531,11 +504,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     /// Codegen an `Rvalue::AddressOf` or `Rvalue::Ref`
     fn codegen_place_to_pointer(
         &mut self,
-        mut bx: Bx,
+        bx: &mut Bx,
         place: mir::Place<'tcx>,
         mk_ptr_ty: impl FnOnce(TyCtxt<'tcx>, Ty<'tcx>) -> Ty<'tcx>,
-    ) -> (Bx, OperandRef<'tcx, Bx::Value>) {
-        let cg_place = self.codegen_place(&mut bx, place.as_ref());
+    ) -> OperandRef<'tcx, Bx::Value> {
+        let cg_place = self.codegen_place(bx, place.as_ref());
 
         let ty = cg_place.layout.ty;
 
@@ -546,7 +519,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         } else {
             OperandValue::Pair(cg_place.llval, cg_place.llextra.unwrap())
         };
-        (bx, OperandRef { val, layout: self.cx.layout_of(mk_ptr_ty(self.cx.tcx(), ty)) })
+        OperandRef { val, layout: self.cx.layout_of(mk_ptr_ty(self.cx.tcx(), ty)) }
     }
 
     pub fn codegen_scalar_binop(
diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs
index 1db0fb3a6f1..19452c8cdc8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/statement.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs
@@ -8,8 +8,8 @@ use crate::traits::*;
 
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     #[instrument(level = "debug", skip(self, bx))]
-    pub fn codegen_statement(&mut self, mut bx: Bx, statement: &mir::Statement<'tcx>) -> Bx {
-        self.set_debug_loc(&mut bx, statement.source_info);
+    pub fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
+        self.set_debug_loc(bx, statement.source_info);
         match statement.kind {
             mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
                 if let Some(index) = place.as_local() {
@@ -19,10 +19,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
                         }
                         LocalRef::Operand(None) => {
-                            let (mut bx, operand) = self.codegen_rvalue_operand(bx, rvalue);
+                            let operand = self.codegen_rvalue_operand(bx, rvalue);
                             self.locals[index] = LocalRef::Operand(Some(operand));
-                            self.debug_introduce_local(&mut bx, index);
-                            bx
+                            self.debug_introduce_local(bx, index);
                         }
                         LocalRef::Operand(Some(op)) => {
                             if !op.layout.is_zst() {
@@ -35,59 +34,52 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
                             // If the type is zero-sized, it's already been set here,
                             // but we still need to make sure we codegen the operand
-                            self.codegen_rvalue_operand(bx, rvalue).0
+                            self.codegen_rvalue_operand(bx, rvalue);
                         }
                     }
                 } else {
-                    let cg_dest = self.codegen_place(&mut bx, place.as_ref());
-                    self.codegen_rvalue(bx, cg_dest, rvalue)
+                    let cg_dest = self.codegen_place(bx, place.as_ref());
+                    self.codegen_rvalue(bx, cg_dest, rvalue);
                 }
             }
             mir::StatementKind::SetDiscriminant { box ref place, variant_index } => {
-                self.codegen_place(&mut bx, place.as_ref())
-                    .codegen_set_discr(&mut bx, variant_index);
-                bx
+                self.codegen_place(bx, place.as_ref()).codegen_set_discr(bx, variant_index);
             }
             mir::StatementKind::Deinit(..) => {
                 // For now, don't codegen this to anything. In the future it may be worth
                 // experimenting with what kind of information we can emit to LLVM without hurting
                 // perf here
-                bx
             }
             mir::StatementKind::StorageLive(local) => {
                 if let LocalRef::Place(cg_place) = self.locals[local] {
-                    cg_place.storage_live(&mut bx);
+                    cg_place.storage_live(bx);
                 } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
-                    cg_indirect_place.storage_live(&mut bx);
+                    cg_indirect_place.storage_live(bx);
                 }
-                bx
             }
             mir::StatementKind::StorageDead(local) => {
                 if let LocalRef::Place(cg_place) = self.locals[local] {
-                    cg_place.storage_dead(&mut bx);
+                    cg_place.storage_dead(bx);
                 } else if let LocalRef::UnsizedPlace(cg_indirect_place) = self.locals[local] {
-                    cg_indirect_place.storage_dead(&mut bx);
+                    cg_indirect_place.storage_dead(bx);
                 }
-                bx
             }
             mir::StatementKind::Coverage(box ref coverage) => {
-                self.codegen_coverage(&mut bx, coverage.clone(), statement.source_info.scope);
-                bx
+                self.codegen_coverage(bx, coverage.clone(), statement.source_info.scope);
             }
             mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => {
-                let op_val = self.codegen_operand(&mut bx, op);
+                let op_val = self.codegen_operand(bx, op);
                 bx.assume(op_val.immediate());
-                bx
             }
             mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
                 mir::CopyNonOverlapping { ref count, ref src, ref dst },
             )) => {
-                let dst_val = self.codegen_operand(&mut bx, dst);
-                let src_val = self.codegen_operand(&mut bx, src);
-                let count = self.codegen_operand(&mut bx, count).immediate();
+                let dst_val = self.codegen_operand(bx, dst);
+                let src_val = self.codegen_operand(bx, src);
+                let count = self.codegen_operand(bx, count).immediate();
                 let pointee_layout = dst_val
                     .layout
-                    .pointee_info_at(&bx, rustc_target::abi::Size::ZERO)
+                    .pointee_info_at(bx, rustc_target::abi::Size::ZERO)
                     .expect("Expected pointer");
                 let bytes = bx.mul(count, bx.const_usize(pointee_layout.size.bytes()));
 
@@ -95,12 +87,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 let dst = dst_val.immediate();
                 let src = src_val.immediate();
                 bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty());
-                bx
             }
             mir::StatementKind::FakeRead(..)
             | mir::StatementKind::Retag { .. }
             | mir::StatementKind::AscribeUserType(..)
-            | mir::StatementKind::Nop => bx,
+            | mir::StatementKind::Nop => {}
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs
index 5006a2157fc..27da33581a1 100644
--- a/compiler/rustc_codegen_ssa/src/mono_item.rs
+++ b/compiler/rustc_codegen_ssa/src/mono_item.rs
@@ -40,12 +40,12 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {
                         .iter()
                         .map(|(op, op_sp)| match *op {
                             hir::InlineAsmOperand::Const { ref anon_const } => {
-                                let anon_const_def_id =
-                                    cx.tcx().hir().local_def_id(anon_const.hir_id).to_def_id();
-                                let const_value =
-                                    cx.tcx().const_eval_poly(anon_const_def_id).unwrap_or_else(
-                                        |_| span_bug!(*op_sp, "asm const cannot be resolved"),
-                                    );
+                                let const_value = cx
+                                    .tcx()
+                                    .const_eval_poly(anon_const.def_id.to_def_id())
+                                    .unwrap_or_else(|_| {
+                                        span_bug!(*op_sp, "asm const cannot be resolved")
+                                    });
                                 let ty = cx
                                     .tcx()
                                     .typeck_body(anon_const.body)
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index a4368303de5..002aaf0db13 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -179,6 +179,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     ("f16c", Some(sym::f16c_target_feature)),
     ("fma", None),
     ("fxsr", None),
+    ("gfni", Some(sym::avx512_target_feature)),
     ("lzcnt", None),
     ("movbe", Some(sym::movbe_target_feature)),
     ("pclmulqdq", None),
@@ -195,6 +196,8 @@ const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
     ("sse4a", Some(sym::sse4a_target_feature)),
     ("ssse3", None),
     ("tbm", Some(sym::tbm_target_feature)),
+    ("vaes", Some(sym::avx512_target_feature)),
+    ("vpclmulqdq", Some(sym::avx512_target_feature)),
     ("xsave", None),
     ("xsavec", None),
     ("xsaveopt", None),
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs
index 01408f39fb3..bc679a5dc87 100644
--- a/compiler/rustc_codegen_ssa/src/traits/builder.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -151,11 +151,11 @@ pub trait BuilderMethods<'a, 'tcx>:
 
     /// Called for Rvalue::Repeat when the elem is neither a ZST nor optimizable using memset.
     fn write_operand_repeatedly(
-        self,
+        &mut self,
         elem: OperandRef<'tcx, Self::Value>,
         count: u64,
         dest: PlaceRef<'tcx, Self::Value>,
-    ) -> Self;
+    );
 
     fn range_metadata(&mut self, load: Self::Value, range: WrappingRange);
     fn nonnull_metadata(&mut self, load: Self::Value);
diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
index f310789d144..63fecaf34fd 100644
--- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
@@ -6,6 +6,8 @@ use rustc_span::{SourceFile, Span, Symbol};
 use rustc_target::abi::call::FnAbi;
 use rustc_target::abi::Size;
 
+use std::ops::Range;
+
 pub trait DebugInfoMethods<'tcx>: BackendTypes {
     fn create_vtable_debuginfo(
         &self,
@@ -72,6 +74,9 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
         direct_offset: Size,
         // NB: each offset implies a deref (i.e. they're steps in a pointer chain).
         indirect_offsets: &[Size],
+        // Byte range in the `dbg_var` covered by this fragment,
+        // if this is a fragment of a composite `DIVariable`.
+        fragment: Option<Range<Size>>,
     );
     fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
     fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 4977a5d6bbf..e3dfd72d5f0 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -55,7 +55,7 @@ impl Error for ConstEvalErrKind {}
 /// When const-evaluation errors, this type is constructed with the resulting information,
 /// and then used to emit the error as a lint or hard error.
 #[derive(Debug)]
-pub struct ConstEvalErr<'tcx> {
+pub(super) struct ConstEvalErr<'tcx> {
     pub span: Span,
     pub error: InterpError<'tcx>,
     pub stacktrace: Vec<FrameInfo<'tcx>>,
@@ -82,8 +82,8 @@ impl<'tcx> ConstEvalErr<'tcx> {
         ConstEvalErr { error: error.into_kind(), stacktrace, span }
     }
 
-    pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
-        self.struct_error(tcx, message, |_| {})
+    pub(super) fn report(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
+        self.report_decorated(tcx, message, |_| {})
     }
 
     /// Create a diagnostic for this const eval error.
@@ -95,7 +95,7 @@ impl<'tcx> ConstEvalErr<'tcx> {
     /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
     /// (Except that for some errors, we ignore all that -- see `must_error` below.)
     #[instrument(skip(self, tcx, decorate), level = "debug")]
-    pub fn struct_error(
+    pub(super) fn report_decorated(
         &self,
         tcx: TyCtxtAt<'tcx>,
         message: &str,
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 1b1052fdf47..f5942deaf9f 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -46,7 +46,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
         ecx.tcx.def_kind(cid.instance.def_id())
     );
     let layout = ecx.layout_of(body.bound_return_ty().subst(tcx, cid.instance.substs))?;
-    assert!(!layout.is_unsized());
+    assert!(layout.is_sized());
     let ret = ecx.allocate(layout, MemoryKind::Stack)?;
 
     trace!(
@@ -255,7 +255,7 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
         return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| {
             let span = tcx.def_span(def_id);
             let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span };
-            error.report_as_error(tcx.at(span), "could not evaluate nullary intrinsic")
+            error.report(tcx.at(span), "could not evaluate nullary intrinsic")
         });
     }
 
@@ -333,7 +333,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
                 }
             };
 
-            Err(err.report_as_error(ecx.tcx.at(err.span), &msg))
+            Err(err.report(ecx.tcx.at(err.span), &msg))
         }
         Ok(mplace) => {
             // Since evaluation had no errors, validate the resulting constant.
@@ -358,7 +358,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
             if let Err(error) = validation {
                 // Validation failed, report an error. This is always a hard error.
                 let err = ConstEvalErr::new(&ecx, error, None);
-                Err(err.struct_error(
+                Err(err.report_decorated(
                     ecx.tcx,
                     "it is undefined behavior to use this value",
                     |diag| {
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 1c33e7845cb..01b2b4b5d9c 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -103,7 +103,7 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
 ) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
     trace!("destructure_mir_constant: {:?}", val);
     let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
-    let op = ecx.const_to_op(&val, None)?;
+    let op = ecx.eval_mir_constant(&val, None, None)?;
 
     // We go to `usize` as we cannot allocate anything bigger anyway.
     let (field_count, variant, down) = match val.ty().kind() {
@@ -139,7 +139,7 @@ pub(crate) fn deref_mir_constant<'tcx>(
     val: mir::ConstantKind<'tcx>,
 ) -> mir::ConstantKind<'tcx> {
     let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
-    let op = ecx.const_to_op(&val, None).unwrap();
+    let op = ecx.eval_mir_constant(&val, None, None).unwrap();
     let mplace = ecx.deref_operand(&op).unwrap();
     if let Some(alloc_id) = mplace.ptr.provenance {
         assert_eq!(
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index f7d64f6d4f4..ed69d8554d2 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -5,7 +5,7 @@ use std::mem;
 use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
 use rustc_index::vec::IndexVec;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo};
+use rustc_middle::mir::interpret::{ErrorHandled, InterpError, InvalidProgramInfo};
 use rustc_middle::ty::layout::{
     self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
     TyAndLayout,
@@ -572,7 +572,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         metadata: &MemPlaceMeta<M::Provenance>,
         layout: &TyAndLayout<'tcx>,
     ) -> InterpResult<'tcx, Option<(Size, Align)>> {
-        if !layout.is_unsized() {
+        if layout.is_sized() {
             return Ok(Some((layout.size, layout.align.abi)));
         }
         match layout.ty.kind() {
@@ -696,12 +696,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         for ct in &body.required_consts {
             let span = ct.span;
             let ct = self.subst_from_current_frame_and_normalize_erasing_regions(ct.literal)?;
-            self.const_to_op(&ct, None).map_err(|err| {
-                // If there was an error, set the span of the current frame to this constant.
-                // Avoiding doing this when evaluation succeeds.
-                self.frame_mut().loc = Err(span);
-                err
-            })?;
+            self.eval_mir_constant(&ct, Some(span), None)?;
         }
 
         // Most locals are initially dead.
@@ -912,9 +907,32 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         Ok(())
     }
 
-    pub fn eval_to_allocation(
+    /// Call a query that can return `ErrorHandled`. If `span` is `Some`, point to that span when an error occurs.
+    pub fn ctfe_query<T>(
+        &self,
+        span: Option<Span>,
+        query: impl FnOnce(TyCtxtAt<'tcx>) -> Result<T, ErrorHandled>,
+    ) -> InterpResult<'tcx, T> {
+        // Use a precise span for better cycle errors.
+        query(self.tcx.at(span.unwrap_or_else(|| self.cur_span()))).map_err(|err| {
+            match err {
+                ErrorHandled::Reported(err) => {
+                    if let Some(span) = span {
+                        // To make it easier to figure out where this error comes from, also add a note at the current location.
+                        self.tcx.sess.span_note_without_error(span, "erroneous constant used");
+                    }
+                    err_inval!(AlreadyReported(err))
+                }
+                ErrorHandled::TooGeneric => err_inval!(TooGeneric),
+            }
+            .into()
+        })
+    }
+
+    pub fn eval_global(
         &self,
         gid: GlobalId<'tcx>,
+        span: Option<Span>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         // For statics we pick `ParamEnv::reveal_all`, because statics don't have generics
         // and thus don't care about the parameter environment. While we could just use
@@ -927,8 +945,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             self.param_env
         };
         let param_env = param_env.with_const();
-        // Use a precise span for better cycle errors.
-        let val = self.tcx.at(self.cur_span()).eval_to_allocation_raw(param_env.and(gid))?;
+        let val = self.ctfe_query(span, |tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?;
         self.raw_const_to_mplace(val)
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 6809a42dc45..458cc6180d5 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -134,7 +134,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval:
         alloc.mutability = Mutability::Not;
     };
     // link the alloc id to the actual allocation
-    leftover_allocations.extend(alloc.provenance().iter().map(|&(_, alloc_id)| alloc_id));
+    leftover_allocations.extend(alloc.provenance().ptrs().iter().map(|&(_, alloc_id)| alloc_id));
     let alloc = tcx.intern_const_alloc(alloc);
     tcx.set_alloc_id_memory(alloc_id, alloc);
     None
@@ -439,7 +439,7 @@ pub fn intern_const_alloc_recursive<
             }
             let alloc = tcx.intern_const_alloc(alloc);
             tcx.set_alloc_id_memory(alloc_id, alloc);
-            for &(_, alloc_id) in alloc.inner().provenance().iter() {
+            for &(_, alloc_id) in alloc.inner().provenance().ptrs().iter() {
                 if leftover_allocations.insert(alloc_id) {
                     todo.push(alloc_id);
                 }
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index b92a6878847..6fc2407b778 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -177,8 +177,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     sym::type_name => self.tcx.mk_static_str(),
                     _ => bug!(),
                 };
-                let val =
-                    self.tcx.const_eval_global_id(self.param_env, gid, Some(self.tcx.span))?;
+                let val = self.ctfe_query(None, |tcx| {
+                    tcx.const_eval_global_id(self.param_env, gid, Some(tcx.span))
+                })?;
                 let val = self.const_val_to_op(val, ty, Some(dest.layout))?;
                 self.copy_op(&val, dest, /*allow_transmute*/ false)?;
             }
@@ -713,7 +714,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         rhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
     ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
         let layout = self.layout_of(lhs.layout.ty.builtin_deref(true).unwrap().ty)?;
-        assert!(!layout.is_unsized());
+        assert!(layout.is_sized());
 
         let get_bytes = |this: &InterpCx<'mir, 'tcx, M>,
                          op: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index e5e015c1e18..528c1cb06c0 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -112,7 +112,7 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
 /// A reference to some allocation that was already bounds-checked for the given region
 /// and had the on-access machine hooks run.
 #[derive(Copy, Clone)]
-pub struct AllocRef<'a, 'tcx, Prov, Extra> {
+pub struct AllocRef<'a, 'tcx, Prov: Provenance, Extra> {
     alloc: &'a Allocation<Prov, Extra>,
     range: AllocRange,
     tcx: TyCtxt<'tcx>,
@@ -120,7 +120,7 @@ pub struct AllocRef<'a, 'tcx, Prov, Extra> {
 }
 /// A reference to some allocation that was already bounds-checked for the given region
 /// and had the on-access machine hooks run.
-pub struct AllocRefMut<'a, 'tcx, Prov, Extra> {
+pub struct AllocRefMut<'a, 'tcx, Prov: Provenance, Extra> {
     alloc: &'a mut Allocation<Prov, Extra>,
     range: AllocRange,
     tcx: TyCtxt<'tcx>,
@@ -302,8 +302,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             .into());
         };
 
-        debug!(?alloc);
-
         if alloc.mutability == Mutability::Not {
             throw_ub_format!("deallocating immutable allocation {alloc_id:?}");
         }
@@ -503,8 +501,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     throw_unsup!(ReadExternStatic(def_id));
                 }
 
-                // Use a precise span for better cycle errors.
-                (self.tcx.at(self.cur_span()).eval_static_initializer(def_id)?, Some(def_id))
+                // We don't give a span -- statics don't need that, they cannot be generic or associated.
+                let val = self.ctfe_query(None, |tcx| tcx.eval_static_initializer(def_id))?;
+                (val, Some(def_id))
             }
         };
         M::before_access_global(*self.tcx, &self.machine, id, alloc, def_id, is_write)?;
@@ -683,7 +682,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // Use size and align of the type.
                 let ty = self.tcx.type_of(def_id);
                 let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
-                assert!(!layout.is_unsized());
+                assert!(layout.is_sized());
                 (layout.size, layout.align.abi, AllocKind::LiveData)
             }
             Some(GlobalAlloc::Memory(alloc)) => {
@@ -797,7 +796,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     // This is a new allocation, add the allocation it points to `todo`.
                     if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
                         todo.extend(
-                            alloc.provenance().values().filter_map(|prov| prov.get_alloc_id()),
+                            alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id()),
                         );
                     }
                 }
@@ -833,7 +832,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
             allocs_to_print: &mut VecDeque<AllocId>,
             alloc: &Allocation<Prov, Extra>,
         ) -> std::fmt::Result {
-            for alloc_id in alloc.provenance().values().filter_map(|prov| prov.get_alloc_id()) {
+            for alloc_id in alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id())
+            {
                 allocs_to_print.push_back(alloc_id);
             }
             write!(fmt, "{}", display_allocation(tcx, alloc))
@@ -962,7 +962,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> {
 
     /// Returns whether the allocation has provenance anywhere in the range of the `AllocRef`.
     pub(crate) fn has_provenance(&self) -> bool {
-        self.alloc.range_has_provenance(&self.tcx, self.range)
+        !self.alloc.provenance().range_empty(self.range, &self.tcx)
     }
 }
 
@@ -1060,7 +1060,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         // Source alloc preparations and access hooks.
         let Some((src_alloc_id, src_offset, src_prov)) = src_parts else {
-            // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do.
+            // Zero-sized *source*, that means dest is also zero-sized and we have nothing to do.
             return Ok(());
         };
         let src_alloc = self.get_alloc_raw(src_alloc_id)?;
@@ -1079,22 +1079,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             return Ok(());
         };
 
-        // Checks provenance edges on the src, which needs to happen before
-        // `prepare_provenance_copy`.
-        if src_alloc.range_has_provenance(&tcx, alloc_range(src_range.start, Size::ZERO)) {
-            throw_unsup!(PartialPointerCopy(Pointer::new(src_alloc_id, src_range.start)));
-        }
-        if src_alloc.range_has_provenance(&tcx, alloc_range(src_range.end(), Size::ZERO)) {
-            throw_unsup!(PartialPointerCopy(Pointer::new(src_alloc_id, src_range.end())));
-        }
+        // Prepare getting source provenance.
         let src_bytes = src_alloc.get_bytes_unchecked(src_range).as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation
         // first copy the provenance to a temporary buffer, because
         // `get_bytes_mut` will clear the provenance, which is correct,
         // since we don't want to keep any provenance at the target.
-        let provenance =
-            src_alloc.prepare_provenance_copy(self, src_range, dest_offset, num_copies);
+        // This will also error if copying partial provenance is not supported.
+        let provenance = src_alloc
+            .provenance()
+            .prepare_copy(src_range, dest_offset, num_copies, self)
+            .map_err(|e| e.to_interp_error(dest_alloc_id))?;
         // Prepare a copy of the initialization mask.
-        let compressed = src_alloc.compress_uninit_range(src_range);
+        let init = src_alloc.init_mask().prepare_copy(src_range);
 
         // Destination alloc preparations and access hooks.
         let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?;
@@ -1111,7 +1107,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             .map_err(|e| e.to_interp_error(dest_alloc_id))?
             .as_mut_ptr();
 
-        if compressed.no_bytes_init() {
+        if init.no_bytes_init() {
             // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range
             // is marked as uninitialized but we otherwise omit changing the byte representation which may
             // be arbitrary for uninitialized bytes.
@@ -1160,13 +1156,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         }
 
         // now fill in all the "init" data
-        dest_alloc.mark_compressed_init_range(
-            &compressed,
+        dest_alloc.init_mask_apply_copy(
+            init,
             alloc_range(dest_offset, size), // just a single copy (i.e., not full `dest_range`)
             num_copies,
         );
         // copy the provenance to the destination
-        dest_alloc.mark_provenance_range(provenance);
+        dest_alloc.provenance_apply_copy(provenance);
 
         Ok(())
     }
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index dd00678aa0c..cf9202540c7 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -4,8 +4,9 @@
 use rustc_hir::def::Namespace;
 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
-use rustc_middle::ty::{ConstInt, Ty};
+use rustc_middle::ty::{ConstInt, Ty, ValTree};
 use rustc_middle::{mir, ty};
+use rustc_span::Span;
 use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding};
 use rustc_target::abi::{VariantIdx, Variants};
 
@@ -280,7 +281,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
         layout: TyAndLayout<'tcx>,
         cx: &impl HasDataLayout,
     ) -> InterpResult<'tcx, Self> {
-        assert!(!layout.is_unsized());
+        assert!(layout.is_sized());
         self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
     }
 }
@@ -376,7 +377,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     /// Read an immediate from a place, asserting that that is possible with the given layout.
     ///
-    /// If this suceeds, the `ImmTy` is never `Uninit`.
+    /// If this succeeds, the `ImmTy` is never `Uninit`.
     #[inline(always)]
     pub fn read_immediate(
         &self,
@@ -527,14 +528,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?,
 
             Constant(ref constant) => {
-                let val =
+                let c =
                     self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
 
                 // This can still fail:
                 // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
                 //   checked yet.
                 // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
-                self.const_to_op(&val, layout)?
+                self.eval_mir_constant(&c, Some(constant.span), layout)?
             }
         };
         trace!("{:?}: {:?}", mir_op, *op);
@@ -549,9 +550,35 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         ops.iter().map(|op| self.eval_operand(op, None)).collect()
     }
 
-    pub fn const_to_op(
+    fn eval_ty_constant(
+        &self,
+        val: ty::Const<'tcx>,
+        span: Option<Span>,
+    ) -> InterpResult<'tcx, ValTree<'tcx>> {
+        Ok(match val.kind() {
+            ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
+                throw_inval!(TooGeneric)
+            }
+            ty::ConstKind::Error(reported) => {
+                throw_inval!(AlreadyReported(reported))
+            }
+            ty::ConstKind::Unevaluated(uv) => {
+                let instance = self.resolve(uv.def, uv.substs)?;
+                let cid = GlobalId { instance, promoted: None };
+                self.ctfe_query(span, |tcx| tcx.eval_to_valtree(self.param_env.and(cid)))?
+                    .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"))
+            }
+            ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => {
+                span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {val:?}")
+            }
+            ty::ConstKind::Value(valtree) => valtree,
+        })
+    }
+
+    pub fn eval_mir_constant(
         &self,
         val: &mir::ConstantKind<'tcx>,
+        span: Option<Span>,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         // FIXME(const_prop): normalization needed b/c const prop lint in
@@ -563,44 +590,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let val = self.tcx.normalize_erasing_regions(self.param_env, *val);
         match val {
             mir::ConstantKind::Ty(ct) => {
-                match ct.kind() {
-                    ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => {
-                        throw_inval!(TooGeneric)
-                    }
-                    ty::ConstKind::Error(reported) => {
-                        throw_inval!(AlreadyReported(reported))
-                    }
-                    ty::ConstKind::Unevaluated(uv) => {
-                        // NOTE: We evaluate to a `ValTree` here as a check to ensure
-                        // we're working with valid constants, even though we never need it.
-                        let instance = self.resolve(uv.def, uv.substs)?;
-                        let cid = GlobalId { instance, promoted: None };
-                        let _valtree = self
-                            .tcx
-                            .eval_to_valtree(self.param_env.and(cid))?
-                            .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"));
-
-                        Ok(self.eval_to_allocation(cid)?.into())
-                    }
-                    ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => {
-                        span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {ct:?}")
-                    }
-                    ty::ConstKind::Value(valtree) => {
-                        let ty = ct.ty();
-                        let const_val = self.tcx.valtree_to_const_val((ty, valtree));
-                        self.const_val_to_op(const_val, ty, layout)
-                    }
-                }
+                let ty = ct.ty();
+                let valtree = self.eval_ty_constant(ct, span)?;
+                let const_val = self.tcx.valtree_to_const_val((ty, valtree));
+                self.const_val_to_op(const_val, ty, layout)
             }
             mir::ConstantKind::Val(val, ty) => self.const_val_to_op(val, ty, layout),
             mir::ConstantKind::Unevaluated(uv, _) => {
                 let instance = self.resolve(uv.def, uv.substs)?;
-                Ok(self.eval_to_allocation(GlobalId { instance, promoted: uv.promoted })?.into())
+                Ok(self.eval_global(GlobalId { instance, promoted: uv.promoted }, span)?.into())
             }
         }
     }
 
-    pub(crate) fn const_val_to_op(
+    pub(super) fn const_val_to_op(
         &self,
         val_val: ConstValue<'tcx>,
         ty: Ty<'tcx>,
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index b0625b5f412..29d2312612e 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -201,7 +201,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
         layout: TyAndLayout<'tcx>,
         cx: &impl HasDataLayout,
     ) -> InterpResult<'tcx, Self> {
-        assert!(!layout.is_unsized());
+        assert!(layout.is_sized());
         self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx)
     }
 
@@ -316,8 +316,7 @@ where
         Ok(MPlaceTy { mplace, layout, align })
     }
 
-    /// Take an operand, representing a pointer, and dereference it to a place -- that
-    /// will always be a MemPlace.  Lives in `place.rs` because it creates a place.
+    /// Take an operand, representing a pointer, and dereference it to a place.
     #[instrument(skip(self), level = "debug")]
     pub fn deref_operand(
         &self,
@@ -331,7 +330,7 @@ where
         }
 
         let mplace = self.ref_to_mplace(&val)?;
-        self.check_mplace_access(mplace, CheckInAllocMsg::DerefTest)?;
+        self.check_mplace(mplace)?;
         Ok(mplace)
     }
 
@@ -340,7 +339,7 @@ where
         &self,
         place: &MPlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
-        assert!(!place.layout.is_unsized());
+        assert!(place.layout.is_sized());
         assert!(!place.meta.has_meta());
         let size = place.layout.size;
         self.get_ptr_alloc(place.ptr, size, place.align)
@@ -351,24 +350,25 @@ where
         &mut self,
         place: &MPlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
-        assert!(!place.layout.is_unsized());
+        assert!(place.layout.is_sized());
         assert!(!place.meta.has_meta());
         let size = place.layout.size;
         self.get_ptr_alloc_mut(place.ptr, size, place.align)
     }
 
     /// Check if this mplace is dereferenceable and sufficiently aligned.
-    fn check_mplace_access(
-        &self,
-        mplace: MPlaceTy<'tcx, M::Provenance>,
-        msg: CheckInAllocMsg,
-    ) -> InterpResult<'tcx> {
+    pub fn check_mplace(&self, mplace: MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
         let (size, align) = self
             .size_and_align_of_mplace(&mplace)?
             .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
         assert!(mplace.align <= align, "dynamic alignment less strict than static one?");
         let align = M::enforce_alignment(self).then_some(align);
-        self.check_ptr_access_align(mplace.ptr, size, align.unwrap_or(Align::ONE), msg)?;
+        self.check_ptr_access_align(
+            mplace.ptr,
+            size,
+            align.unwrap_or(Align::ONE),
+            CheckInAllocMsg::DerefTest,
+        )?;
         Ok(())
     }
 
@@ -485,7 +485,7 @@ where
         src: Immediate<M::Provenance>,
         dest: &PlaceTy<'tcx, M::Provenance>,
     ) -> InterpResult<'tcx> {
-        assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
+        assert!(dest.layout.is_sized(), "Cannot write unsized data");
         trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
 
         // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
@@ -746,7 +746,7 @@ where
         layout: TyAndLayout<'tcx>,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
-        assert!(!layout.is_unsized());
+        assert!(layout.is_sized());
         let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?;
         Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
     }
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index c6e04cbfb6b..3c286fa61be 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -209,7 +209,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
             Repeat(ref operand, _) => {
                 let src = self.eval_operand(operand, None)?;
-                assert!(!src.layout.is_unsized());
+                assert!(src.layout.is_sized());
                 let dest = self.force_allocation(&dest)?;
                 let length = dest.len(self)?;
 
diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs
index cab23b7241f..fa15d466ac1 100644
--- a/compiler/rustc_const_eval/src/interpret/traits.rs
+++ b/compiler/rustc_const_eval/src/interpret/traits.rs
@@ -53,7 +53,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> InterpResult<'tcx, (Size, Align)> {
         let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?;
         let layout = self.layout_of(ty)?;
-        assert!(!layout.is_unsized(), "there are no vtables for unsized types");
+        assert!(layout.is_sized(), "there are no vtables for unsized types");
         Ok((layout.size, layout.align.abi))
     }
 }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0322.md b/compiler/rustc_error_codes/src/error_codes/E0322.md
index ccef8681dd6..194bbd83b0f 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0322.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0322.md
@@ -1,4 +1,5 @@
-The `Sized` trait was implemented explicitly.
+A built-in trait was implemented explicitly. All implementations of the trait
+are provided automatically by the compiler.
 
 Erroneous code example:
 
diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl
index ad0d7582101..70ce559526c 100644
--- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl
@@ -182,3 +182,7 @@ codegen_ssa_extract_bundled_libs_write_file = failed to write file '{$rlib}': {$
 codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}`
 
 codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {error}
+
+codegen_ssa_read_file = failed to read file: {message}
+
+codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl
index 6d42b23fb3a..4c7ce30097c 100644
--- a/compiler/rustc_error_messages/locales/en-US/parser.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl
@@ -375,3 +375,15 @@ parser_async_move_order_incorrect = the order of `move` and `async` is incorrect
 
 parser_double_colon_in_bound = expected `:` followed by trait or lifetime
     .suggestion = use single colon
+
+parser_fn_ptr_with_generics = function pointer types may not have generic parameters
+    .suggestion = consider moving the lifetime {$arity ->
+        [one] parameter
+        *[other] parameters
+    } to {$for_param_list_exists ->
+        [true] the
+        *[false] a
+    } `for` parameter list
+
+parser_invalid_identifier_with_leading_number = expected identifier, found number literal
+    .label = identifiers cannot start with a number
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 375e3ebd77d..66c986977ec 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -64,8 +64,7 @@ impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> {
 
 /// Trait implemented by error types. This should not be implemented manually. Instead, use
 /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
-#[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")]
-#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "AddToDiagnostic")]
+#[rustc_diagnostic_item = "AddToDiagnostic"]
 pub trait AddToDiagnostic
 where
     Self: Sized,
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 18cbf963494..a2ed988643f 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -16,8 +16,7 @@ use std::thread::panicking;
 
 /// Trait implemented by error types. This should not be implemented manually. Instead, use
 /// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
-#[cfg_attr(bootstrap, rustc_diagnostic_item = "SessionDiagnostic")]
-#[cfg_attr(not(bootstrap), rustc_diagnostic_item = "IntoDiagnostic")]
+#[rustc_diagnostic_item = "IntoDiagnostic"]
 pub trait IntoDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
     /// Write out as a diagnostic out of `Handler`.
     #[must_use]
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index a8fd1a17a51..170d4341ae7 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -1254,6 +1254,10 @@ impl HandlerInner {
         }
 
         if diagnostic.has_future_breakage() {
+            // Future breakages aren't emitted if they're Level::Allowed,
+            // but they still need to be constructed and stashed below,
+            // so they'll trigger the good-path bug check.
+            self.suppressed_expected_diag = true;
             self.future_breakage_diagnostics.push(diagnostic.clone());
         }
 
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 1294f1e17d4..95fff929d46 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1226,10 +1226,10 @@ pub fn expr_to_spanned_string<'a>(
     let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
 
     Err(match expr.kind {
-        ast::ExprKind::Lit(ref l) => match l.kind {
-            ast::LitKind::Str(s, style) => return Ok((s, style, expr.span)),
-            ast::LitKind::ByteStr(_) => {
-                let mut err = cx.struct_span_err(l.span, err_msg);
+        ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
+            Ok(ast::LitKind::Str(s, style)) => return Ok((s, style, expr.span)),
+            Ok(ast::LitKind::ByteStr(_)) => {
+                let mut err = cx.struct_span_err(expr.span, err_msg);
                 let span = expr.span.shrink_to_lo();
                 err.span_suggestion(
                     span.with_hi(span.lo() + BytePos(1)),
@@ -1239,8 +1239,9 @@ pub fn expr_to_spanned_string<'a>(
                 );
                 Some((err, true))
             }
-            ast::LitKind::Err => None,
-            _ => Some((cx.struct_span_err(l.span, err_msg), false)),
+            Ok(ast::LitKind::Err) => None,
+            Err(_) => None,
+            _ => Some((cx.struct_span_err(expr.span, err_msg), false)),
         },
         ast::ExprKind::Err => None,
         _ => Some((cx.struct_span_err(expr.span, err_msg), false)),
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 0952e65cfee..8aa72e142f8 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -334,8 +334,8 @@ impl<'a> ExtCtxt<'a> {
     }
 
     fn expr_lit(&self, span: Span, lit_kind: ast::LitKind) -> P<ast::Expr> {
-        let lit = ast::Lit::from_lit_kind(lit_kind, span);
-        self.expr(span, ast::ExprKind::Lit(lit))
+        let token_lit = lit_kind.to_token_lit();
+        self.expr(span, ast::ExprKind::Lit(token_lit))
     }
 
     pub fn expr_usize(&self, span: Span, i: usize) -> P<ast::Expr> {
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 95cec8d7ae2..d161868edce 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -76,6 +76,7 @@ pub(crate) use ParseResult::*;
 use crate::mbe::{macro_rules::Tracker, KleeneOp, TokenTree};
 
 use rustc_ast::token::{self, DocComment, Nonterminal, NonterminalKind, Token};
+use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::ErrorGuaranteed;
@@ -86,6 +87,7 @@ use rustc_span::symbol::MacroRulesNormalizedIdent;
 use rustc_span::Span;
 use std::borrow::Cow;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
+use std::fmt::Display;
 
 /// A unit within a matcher that a `MatcherPos` can refer to. Similar to (and derived from)
 /// `mbe::TokenTree`, but designed specifically for fast and easy traversal during matching.
@@ -96,7 +98,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
 ///
 /// This means a matcher can be represented by `&[MatcherLoc]`, and traversal mostly involves
 /// simply incrementing the current matcher position index by one.
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Clone)]
 pub(crate) enum MatcherLoc {
     Token {
         token: Token,
@@ -129,6 +131,46 @@ pub(crate) enum MatcherLoc {
     Eof,
 }
 
+impl MatcherLoc {
+    pub(super) fn span(&self) -> Option<Span> {
+        match self {
+            MatcherLoc::Token { token } => Some(token.span),
+            MatcherLoc::Delimited => None,
+            MatcherLoc::Sequence { .. } => None,
+            MatcherLoc::SequenceKleeneOpNoSep { .. } => None,
+            MatcherLoc::SequenceSep { .. } => None,
+            MatcherLoc::SequenceKleeneOpAfterSep { .. } => None,
+            MatcherLoc::MetaVarDecl { span, .. } => Some(*span),
+            MatcherLoc::Eof => None,
+        }
+    }
+}
+
+impl Display for MatcherLoc {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            MatcherLoc::Token { token } | MatcherLoc::SequenceSep { separator: token } => {
+                write!(f, "`{}`", pprust::token_to_string(token))
+            }
+            MatcherLoc::MetaVarDecl { bind, kind, .. } => {
+                write!(f, "meta-variable `${bind}")?;
+                if let Some(kind) = kind {
+                    write!(f, ":{}", kind)?;
+                }
+                write!(f, "`")?;
+                Ok(())
+            }
+            MatcherLoc::Eof => f.write_str("end of macro"),
+
+            // These are not printed in the diagnostic
+            MatcherLoc::Delimited => f.write_str("delimiter"),
+            MatcherLoc::Sequence { .. } => f.write_str("sequence start"),
+            MatcherLoc::SequenceKleeneOpNoSep { .. } => f.write_str("sequence end"),
+            MatcherLoc::SequenceKleeneOpAfterSep { .. } => f.write_str("sequence end"),
+        }
+    }
+}
+
 pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
     fn inner(
         tts: &[TokenTree],
@@ -398,6 +440,10 @@ impl TtParser {
         }
     }
 
+    pub(super) fn has_no_remaining_items_for_step(&self) -> bool {
+        self.cur_mps.is_empty()
+    }
+
     /// Process the matcher positions of `cur_mps` until it is empty. In the process, this will
     /// produce more mps in `next_mps` and `bb_mps`.
     ///
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 99af9107288..c02680b77fb 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -22,7 +22,7 @@ use rustc_lint_defs::builtin::{
     RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
 };
 use rustc_lint_defs::BuiltinLintDiagnostics;
-use rustc_parse::parser::Parser;
+use rustc_parse::parser::{Parser, Recovery};
 use rustc_session::parse::ParseSess;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
@@ -219,6 +219,8 @@ pub(super) trait Tracker<'matcher> {
 
     /// For tracing.
     fn description() -> &'static str;
+
+    fn recovery() -> Recovery;
 }
 
 /// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to monomorphization.
@@ -230,6 +232,9 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
     fn description() -> &'static str {
         "none"
     }
+    fn recovery() -> Recovery {
+        Recovery::Forbidden
+    }
 }
 
 /// Expands the rules based macro defined by `lhses` and `rhses` for a given
@@ -330,15 +335,20 @@ fn expand_macro<'cx>(
     let mut tracker = CollectTrackerAndEmitter::new(cx, sp);
 
     let try_success_result = try_match_macro(sess, name, &arg, lhses, &mut tracker);
-    assert!(try_success_result.is_err(), "Macro matching returned a success on the second try");
+
+    if try_success_result.is_ok() {
+        // Nonterminal parser recovery might turn failed matches into successful ones,
+        // but for that it must have emitted an error already
+        tracker.cx.sess.delay_span_bug(sp, "Macro matching returned a success on the second try");
+    }
 
     if let Some(result) = tracker.result {
-        // An irrecoverable error occured and has been emitted.
+        // An irrecoverable error occurred and has been emitted.
         return result;
     }
 
-    let Some((token, label)) = tracker.best_failure else {
-        return tracker.result.expect("must have encountered Error or ErrorReported");
+    let Some((token, label, remaining_matcher)) = tracker.best_failure else {
+        return DummyResult::any(sp);
     };
 
     let span = token.span.substitute_dummy(sp);
@@ -351,10 +361,16 @@ fn expand_macro<'cx>(
 
     annotate_doc_comment(&mut err, sess.source_map(), span);
 
+    if let Some(span) = remaining_matcher.span() {
+        err.span_note(span, format!("while trying to match {remaining_matcher}"));
+    } else {
+        err.note(format!("while trying to match {remaining_matcher}"));
+    }
+
     // 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 {
-            let parser = parser_from_cx(sess, arg.clone());
+            let parser = parser_from_cx(sess, arg.clone(), Recovery::Allowed);
             let mut tt_parser = TtParser::new(name);
 
             if let Success(_) =
@@ -379,27 +395,45 @@ fn expand_macro<'cx>(
 }
 
 /// The tracker used for the slow error path that collects useful info for diagnostics.
-struct CollectTrackerAndEmitter<'a, 'cx> {
+struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
     cx: &'a mut ExtCtxt<'cx>,
+    remaining_matcher: Option<&'matcher MatcherLoc>,
     /// Which arm's failure should we report? (the one furthest along)
-    best_failure: Option<(Token, &'static str)>,
+    best_failure: Option<(Token, &'static str, MatcherLoc)>,
     root_span: Span,
     result: Option<Box<dyn MacResult + 'cx>>,
 }
 
-impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx> {
-    fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {
-        // Empty for now.
+impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
+    fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) {
+        if self.remaining_matcher.is_none()
+            || (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof)
+        {
+            self.remaining_matcher = Some(matcher);
+        }
     }
 
     fn after_arm(&mut self, result: &NamedParseResult) {
         match result {
             Success(_) => {
-                unreachable!("should not collect detailed info for successful macro match");
+                // Nonterminal parser recovery might turn failed matches into successful ones,
+                // but for that it must have emitted an error already
+                self.cx.sess.delay_span_bug(
+                    self.root_span,
+                    "should not collect detailed info for successful macro match",
+                );
             }
             Failure(token, msg) => match self.best_failure {
-                Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {}
-                _ => self.best_failure = Some((token.clone(), msg)),
+                Some((ref best_token, _, _)) if best_token.span.lo() >= token.span.lo() => {}
+                _ => {
+                    self.best_failure = Some((
+                        token.clone(),
+                        msg,
+                        self.remaining_matcher
+                            .expect("must have collected matcher already")
+                            .clone(),
+                    ))
+                }
             },
             Error(err_sp, msg) => {
                 let span = err_sp.substitute_dummy(self.root_span);
@@ -413,11 +447,15 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx>
     fn description() -> &'static str {
         "detailed"
     }
+
+    fn recovery() -> Recovery {
+        Recovery::Allowed
+    }
 }
 
-impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx> {
+impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> {
     fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self {
-        Self { cx, best_failure: None, root_span, result: None }
+        Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None }
     }
 }
 
@@ -458,7 +496,7 @@ fn try_match_macro<'matcher, T: Tracker<'matcher>>(
     // 68836 suggests a more comprehensive but more complex change to deal with
     // this situation.)
     // FIXME(Nilstrieb): Stop recovery from happening on this parser and retry later with recovery if the macro failed to match.
-    let parser = parser_from_cx(sess, arg.clone());
+    let parser = parser_from_cx(sess, arg.clone(), T::recovery());
     // Try each arm's matchers.
     let mut tt_parser = TtParser::new(name);
     for (i, lhs) in lhses.iter().enumerate() {
@@ -1540,8 +1578,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
     }
 }
 
-fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> {
-    Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
+fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> {
+    Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS).recovery(recovery)
 }
 
 /// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index a929f6cb0a5..2e832deeecd 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -516,14 +516,14 @@ impl server::TokenStream for Rustc<'_, '_> {
         // We don't use `TokenStream::from_ast` as the tokenstream currently cannot
         // be recovered in the general case.
         match &expr.kind {
-            ast::ExprKind::Lit(l) if l.token_lit.kind == token::Bool => {
+            ast::ExprKind::Lit(token_lit) if token_lit.kind == token::Bool => {
                 Ok(tokenstream::TokenStream::token_alone(
-                    token::Ident(l.token_lit.symbol, false),
-                    l.span,
+                    token::Ident(token_lit.symbol, false),
+                    expr.span,
                 ))
             }
-            ast::ExprKind::Lit(l) => {
-                Ok(tokenstream::TokenStream::token_alone(token::Literal(l.token_lit), l.span))
+            ast::ExprKind::Lit(token_lit) => {
+                Ok(tokenstream::TokenStream::token_alone(token::Literal(*token_lit), expr.span))
             }
             ast::ExprKind::IncludedBytes(bytes) => {
                 let lit = ast::Lit::from_included_bytes(bytes, expr.span);
@@ -533,16 +533,13 @@ impl server::TokenStream for Rustc<'_, '_> {
                 ))
             }
             ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind {
-                ast::ExprKind::Lit(l) => match l.token_lit {
+                ast::ExprKind::Lit(token_lit) => match token_lit {
                     token::Lit { kind: token::Integer | token::Float, .. } => {
                         Ok(Self::TokenStream::from_iter([
                             // FIXME: The span of the `-` token is lost when
                             // parsing, so we cannot faithfully recover it here.
                             tokenstream::TokenTree::token_alone(token::BinOp(token::Minus), e.span),
-                            tokenstream::TokenTree::token_alone(
-                                token::Literal(l.token_lit),
-                                l.span,
-                            ),
+                            tokenstream::TokenTree::token_alone(token::Literal(*token_lit), e.span),
                         ]))
                     }
                     _ => Err(()),
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index c2aa096a993..1646727a1c8 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -54,9 +54,9 @@ declare_features! (
     /// Allows using ADX intrinsics from `core::arch::{x86, x86_64}`.
     (accepted, adx_target_feature, "1.61.0", Some(44839), None),
     /// Allows explicit discriminants on non-unit enum variants.
-    (accepted, arbitrary_enum_discriminant, "CURRENT_RUSTC_VERSION", Some(60553), None),
+    (accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553), None),
     /// Allows using `sym` operands in inline assembly.
-    (accepted, asm_sym, "CURRENT_RUSTC_VERSION", Some(93333), None),
+    (accepted, asm_sym, "1.66.0", Some(93333), None),
     /// Allows the definition of associated constants in `trait` or `impl` blocks.
     (accepted, associated_consts, "1.20.0", Some(29646), None),
     /// Allows using associated `type`s in `trait`s.
@@ -174,7 +174,7 @@ declare_features! (
     // FIXME: explain `globs`.
     (accepted, globs, "1.0.0", None, None),
     /// Allows using `..=X` as a pattern.
-    (accepted, half_open_range_patterns, "CURRENT_RUSTC_VERSION", Some(67264), None),
+    (accepted, half_open_range_patterns, "1.66.0", Some(67264), None),
     /// Allows using the `u128` and `i128` types.
     (accepted, i128_type, "1.26.0", Some(35118), None),
     /// Allows the use of `if let` expressions.
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index a0e16b18061..5cf2fdde392 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -311,7 +311,7 @@ declare_features! (
     /// Allows `async || body` closures.
     (active, async_closure, "1.37.0", Some(62290), None),
     /// Alows async functions to be declared, implemented, and used in traits.
-    (incomplete, async_fn_in_trait, "CURRENT_RUSTC_VERSION", Some(91611), None),
+    (incomplete, async_fn_in_trait, "1.66.0", Some(91611), None),
     /// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
     (active, c_unwind, "1.52.0", Some(74990), None),
     /// Allows using C-variadics.
@@ -414,7 +414,7 @@ declare_features! (
     /// Allows non-trivial generic constants which have to have wfness manually propagated to callers
     (incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
     /// Allows using `..=X` as a patterns in slices.
-    (active, half_open_range_patterns_in_slices, "CURRENT_RUSTC_VERSION", Some(67264), None),
+    (active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None),
     /// Allows `if let` guard in match arms.
     (active, if_let_guard, "1.47.0", Some(51114), None),
     /// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index dc3a7495684..4b6e068db43 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -147,7 +147,7 @@ pub enum AttributeDuplicates {
     FutureWarnPreceding,
 }
 
-/// A conveniece macro to deal with `$($expr)?`.
+/// A convenience macro to deal with `$($expr)?`.
 macro_rules! or_default {
     ($default:expr,) => {
         $default
@@ -692,6 +692,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
     ),
     rustc_attr!(
+        rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
+        "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
+    ),
+    rustc_attr!(
         rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
         "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
          the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs
index 3c1bb553266..401d3f6689c 100644
--- a/compiler/rustc_graphviz/src/lib.rs
+++ b/compiler/rustc_graphviz/src/lib.rs
@@ -471,7 +471,11 @@ pub trait Labeller<'a> {
 /// Escape tags in such a way that it is suitable for inclusion in a
 /// Graphviz HTML label.
 pub fn escape_html(s: &str) -> String {
-    s.replace('&', "&amp;").replace('\"', "&quot;").replace('<', "&lt;").replace('>', "&gt;")
+    s.replace('&', "&amp;")
+        .replace('\"', "&quot;")
+        .replace('<', "&lt;")
+        .replace('>', "&gt;")
+        .replace('\n', "<br align=\"left\"/>")
 }
 
 impl<'a> LabelText<'a> {
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 82e260d158b..7d8b859a6b4 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -487,6 +487,7 @@ pub enum GenericParamKind<'hir> {
 #[derive(Debug, HashStable_Generic)]
 pub struct GenericParam<'hir> {
     pub hir_id: HirId,
+    pub def_id: LocalDefId,
     pub name: ParamName,
     pub span: Span,
     pub pure_wrt_drop: bool,
@@ -921,6 +922,7 @@ pub struct Crate<'hir> {
 
 #[derive(Debug, HashStable_Generic)]
 pub struct Closure<'hir> {
+    pub def_id: LocalDefId,
     pub binder: ClosureBinder,
     pub capture_clause: CaptureBy,
     pub bound_generic_params: &'hir [GenericParam<'hir>],
@@ -1615,7 +1617,7 @@ pub enum ArrayLen {
 impl ArrayLen {
     pub fn hir_id(&self) -> HirId {
         match self {
-            &ArrayLen::Infer(hir_id, _) | &ArrayLen::Body(AnonConst { hir_id, body: _ }) => hir_id,
+            &ArrayLen::Infer(hir_id, _) | &ArrayLen::Body(AnonConst { hir_id, .. }) => hir_id,
         }
     }
 }
@@ -1627,10 +1629,11 @@ impl ArrayLen {
 /// explicit discriminant values for enum variants.
 ///
 /// You can check if this anon const is a default in a const param
-/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_hir_id(..)`
+/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_def_id(..)`
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)]
 pub struct AnonConst {
     pub hir_id: HirId,
+    pub def_id: LocalDefId,
     pub body: BodyId,
 }
 
@@ -2798,7 +2801,8 @@ pub struct Variant<'hir> {
     /// Name of the variant.
     pub ident: Ident,
     /// Id of the variant (not the constructor, see `VariantData::ctor_hir_id()`).
-    pub id: HirId,
+    pub hir_id: HirId,
+    pub def_id: LocalDefId,
     /// Fields and constructor id of the variant.
     pub data: VariantData<'hir>,
     /// Explicit discriminant (e.g., `Foo = 1`).
@@ -2865,6 +2869,7 @@ pub struct FieldDef<'hir> {
     pub vis_span: Span,
     pub ident: Ident,
     pub hir_id: HirId,
+    pub def_id: LocalDefId,
     pub ty: &'hir Ty<'hir>,
 }
 
@@ -2886,11 +2891,11 @@ pub enum VariantData<'hir> {
     /// A tuple variant.
     ///
     /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
-    Tuple(&'hir [FieldDef<'hir>], HirId),
+    Tuple(&'hir [FieldDef<'hir>], HirId, LocalDefId),
     /// A unit variant.
     ///
     /// E.g., `Bar = ..` as in `enum Foo { Bar = .. }`.
-    Unit(HirId),
+    Unit(HirId, LocalDefId),
 }
 
 impl<'hir> VariantData<'hir> {
@@ -2902,11 +2907,19 @@ impl<'hir> VariantData<'hir> {
         }
     }
 
+    /// Return the `LocalDefId` of this variant's constructor, if it has one.
+    pub fn ctor_def_id(&self) -> Option<LocalDefId> {
+        match *self {
+            VariantData::Struct(_, _) => None,
+            VariantData::Tuple(_, _, def_id) | VariantData::Unit(_, def_id) => Some(def_id),
+        }
+    }
+
     /// Return the `HirId` of this variant's constructor, if it has one.
     pub fn ctor_hir_id(&self) -> Option<HirId> {
         match *self {
             VariantData::Struct(_, _) => None,
-            VariantData::Tuple(_, hir_id) | VariantData::Unit(hir_id) => Some(hir_id),
+            VariantData::Tuple(_, hir_id, _) | VariantData::Unit(hir_id, _) => Some(hir_id),
         }
     }
 }
@@ -3532,7 +3545,7 @@ impl<'hir> Node<'hir> {
     /// Get the fields for the tuple-constructor,
     /// if this node is a tuple constructor, otherwise None
     pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> {
-        if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None }
+        if let Node::Ctor(&VariantData::Tuple(fields, _, _)) = self { Some(fields) } else { None }
     }
 }
 
@@ -3548,7 +3561,7 @@ mod size_asserts {
     static_assert_size!(FnDecl<'_>, 40);
     static_assert_size!(ForeignItem<'_>, 72);
     static_assert_size!(ForeignItemKind<'_>, 40);
-    static_assert_size!(GenericArg<'_>, 24);
+    static_assert_size!(GenericArg<'_>, 32);
     static_assert_size!(GenericBound<'_>, 48);
     static_assert_size!(Generics<'_>, 56);
     static_assert_size!(Impl<'_>, 80);
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 3ef58d7d705..48db93fde9d 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -733,6 +733,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
             walk_list!(visitor, visit_arm, arms);
         }
         ExprKind::Closure(&Closure {
+            def_id: _,
             binder: _,
             bound_generic_params,
             fn_decl,
@@ -1084,7 +1085,7 @@ pub fn walk_enum_def<'v, V: Visitor<'v>>(
 
 pub fn walk_variant<'v, V: Visitor<'v>>(visitor: &mut V, variant: &'v Variant<'v>) {
     visitor.visit_ident(variant.ident);
-    visitor.visit_id(variant.id);
+    visitor.visit_id(variant.hir_id);
     visitor.visit_variant_data(&variant.data);
     walk_list!(visitor, visit_anon_const, &variant.disr_expr);
 }
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 4518cf30acd..16c40cf1299 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -432,7 +432,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         ty::Const::from_opt_const_arg_anon_const(
                             tcx,
                             ty::WithOptConstParam {
-                                did: tcx.hir().local_def_id(ct.value.hir_id),
+                                did: ct.value.def_id,
                                 const_param_did: Some(param.def_id),
                             },
                         )
@@ -570,8 +570,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into())
                         }
                         hir::Term::Const(ref c) => {
-                            let local_did = self.tcx().hir().local_def_id(c.hir_id);
-                            let c = Const::from_anon_const(self.tcx(), local_did);
+                            let c = Const::from_anon_const(self.tcx(), c.def_id);
                             ConvertedBindingKind::Equality(c.into())
                         }
                     },
@@ -856,7 +855,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         &self,
         bounds: &mut Bounds<'hir>,
         ast_bounds: &'hir [hir::GenericBound<'hir>],
-        self_ty_where_predicates: Option<(hir::HirId, &'hir [hir::WherePredicate<'hir>])>,
+        self_ty_where_predicates: Option<(LocalDefId, &'hir [hir::WherePredicate<'hir>])>,
         span: Span,
     ) {
         let tcx = self.tcx();
@@ -876,10 +875,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         };
         search_bounds(ast_bounds);
         if let Some((self_ty, where_clause)) = self_ty_where_predicates {
-            let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id();
             for clause in where_clause {
                 if let hir::WherePredicate::BoundPredicate(pred) = clause {
-                    if pred.is_param_bound(self_ty_def_id) {
+                    if pred.is_param_bound(self_ty.to_def_id()) {
                         search_bounds(pred.bounds);
                     }
                 }
@@ -1917,17 +1915,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
 
             // see if we can satisfy using an inherent associated type
-            for impl_ in tcx.inherent_impls(adt_def.did()) {
-                let assoc_ty = tcx.associated_items(impl_).find_by_name_and_kind(
-                    tcx,
-                    assoc_ident,
-                    ty::AssocKind::Type,
-                    *impl_,
-                );
-                if let Some(assoc_ty) = assoc_ty {
-                    let ty = tcx.type_of(assoc_ty.def_id);
-                    return Ok((ty, DefKind::AssocTy, assoc_ty.def_id));
-                }
+            for &impl_ in tcx.inherent_impls(adt_def.did()) {
+                let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else {
+                    continue;
+                };
+                // FIXME(inherent_associated_types): This does not substitute parameters.
+                let ty = tcx.type_of(assoc_ty_did);
+                return Ok((ty, DefKind::AssocTy, assoc_ty_did));
             }
         }
 
@@ -2014,37 +2008,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         };
 
         let trait_did = bound.def_id();
-        let (assoc_ident, def_scope) =
-            tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id);
-
-        // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
-        // of calling `filter_by_name_and_kind`.
-        let item = tcx.associated_items(trait_did).in_definition_order().find(|i| {
-            i.kind.namespace() == Namespace::TypeNS
-                && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
-        });
-        // Assume that if it's not matched, there must be a const defined with the same name
-        // but it was used in a type position.
-        let Some(item) = item else {
+        let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did) else {
+            // Assume that if it's not matched, there must be a const defined with the same name
+            // but it was used in a type position.
             let msg = format!("found associated const `{assoc_ident}` when type was expected");
             let guar = tcx.sess.struct_span_err(span, &msg).emit();
             return Err(guar);
         };
 
-        let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound);
+        let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound);
         let ty = self.normalize_ty(span, ty);
 
-        let kind = DefKind::AssocTy;
-        if !item.visibility(tcx).is_accessible_from(def_scope, tcx) {
-            let kind = kind.descr(item.def_id);
-            let msg = format!("{} `{}` is private", kind, assoc_ident);
-            tcx.sess
-                .struct_span_err(span, &msg)
-                .span_label(span, &format!("private {}", kind))
-                .emit();
-        }
-        tcx.check_stability(item.def_id, Some(hir_ref_id), span, None);
-
         if let Some(variant_def_id) = variant_resolution {
             tcx.struct_span_lint_hir(
                 AMBIGUOUS_ASSOCIATED_ITEMS,
@@ -2063,7 +2037,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     };
 
                     could_refer_to(DefKind::Variant, variant_def_id, "");
-                    could_refer_to(kind, item.def_id, " also");
+                    could_refer_to(DefKind::AssocTy, assoc_ty_did, " also");
 
                     lint.span_suggestion(
                         span,
@@ -2076,7 +2050,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 },
             );
         }
-        Ok((ty, kind, item.def_id))
+        Ok((ty, DefKind::AssocTy, assoc_ty_did))
+    }
+
+    fn lookup_assoc_ty(
+        &self,
+        ident: Ident,
+        block: hir::HirId,
+        span: Span,
+        scope: DefId,
+    ) -> Option<DefId> {
+        let tcx = self.tcx();
+        let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block);
+
+        // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
+        // of calling `find_by_name_and_kind`.
+        let item = tcx.associated_items(scope).in_definition_order().find(|i| {
+            i.kind.namespace() == Namespace::TypeNS
+                && i.ident(tcx).normalize_to_macros_2_0() == ident
+        })?;
+
+        let kind = DefKind::AssocTy;
+        if !item.visibility(tcx).is_accessible_from(def_scope, tcx) {
+            let kind = kind.descr(item.def_id);
+            let msg = format!("{kind} `{ident}` is private");
+            let def_span = self.tcx().def_span(item.def_id);
+            tcx.sess
+                .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624))
+                .span_label(span, &format!("private {kind}"))
+                .span_label(def_span, &format!("{kind} defined here"))
+                .emit();
+        }
+        tcx.check_stability(item.def_id, Some(block), span, None);
+
+        Some(item.def_id)
     }
 
     fn qpath_to_ty(
@@ -2713,8 +2720,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let length = match length {
                     &hir::ArrayLen::Infer(_, span) => self.ct_infer(tcx.types.usize, None, span),
                     hir::ArrayLen::Body(constant) => {
-                        let length_def_id = tcx.hir().local_def_id(constant.hir_id);
-                        ty::Const::from_anon_const(tcx, length_def_id)
+                        ty::Const::from_anon_const(tcx, constant.def_id)
                     }
                 };
 
@@ -2722,7 +2728,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 self.normalize_ty(ast_ty.span, array_ty)
             }
             hir::TyKind::Typeof(ref e) => {
-                let ty_erased = tcx.type_of(tcx.hir().local_def_id(e.hir_id));
+                let ty_erased = tcx.type_of(e.def_id);
                 let ty = tcx.fold_regions(ty_erased, |r, _| {
                     if r.is_erased() { tcx.lifetimes.re_static } else { r }
                 });
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 2e7b1025764..2fdfdd77256 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -159,7 +159,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
     // the consumer's responsibility to ensure all bytes that have been read
     // have defined values.
     if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
-        && alloc.inner().provenance().len() != 0
+        && alloc.inner().provenance().ptrs().len() != 0
     {
         let msg = "statics with a custom `#[link_section]` must be a \
                         simple list of bytes on the wasm target with no \
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 837ff0bdf3e..efb34e4ff65 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -853,7 +853,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
 
         // Const parameters are well formed if their type is structural match.
         hir::GenericParamKind::Const { ty: hir_ty, default: _ } => {
-            let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id));
+            let ty = tcx.type_of(param.def_id);
 
             if tcx.features().adt_const_params {
                 if let Some(non_structural_match_ty) =
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index ae9ebe59091..1bf3768fead 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -5,10 +5,11 @@
 // done by the orphan and overlap modules. Then we build up various
 // mappings. That mapping code resides here.
 
-use rustc_errors::struct_span_err;
+use rustc_errors::{error_code, struct_span_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
+use rustc_span::sym;
 use rustc_trait_selection::traits;
 
 mod builtin;
@@ -39,61 +40,26 @@ fn enforce_trait_manually_implementable(
     impl_def_id: LocalDefId,
     trait_def_id: DefId,
 ) {
-    let did = Some(trait_def_id);
-    let li = tcx.lang_items();
     let impl_header_span = tcx.def_span(impl_def_id);
 
-    // Disallow *all* explicit impls of `Pointee`, `DiscriminantKind`, `Sized` and `Unsize` for now.
-    if did == li.pointee_trait() {
-        struct_span_err!(
+    // Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]`
+    if tcx.has_attr(trait_def_id, sym::rustc_deny_explicit_impl) {
+        let trait_name = tcx.item_name(trait_def_id);
+        let mut err = struct_span_err!(
             tcx.sess,
             impl_header_span,
             E0322,
-            "explicit impls for the `Pointee` trait are not permitted"
-        )
-        .span_label(impl_header_span, "impl of `Pointee` not allowed")
-        .emit();
-        return;
-    }
-
-    if did == li.discriminant_kind_trait() {
-        struct_span_err!(
-            tcx.sess,
-            impl_header_span,
-            E0322,
-            "explicit impls for the `DiscriminantKind` trait are not permitted"
-        )
-        .span_label(impl_header_span, "impl of `DiscriminantKind` not allowed")
-        .emit();
-        return;
-    }
-
-    if did == li.sized_trait() {
-        struct_span_err!(
-            tcx.sess,
-            impl_header_span,
-            E0322,
-            "explicit impls for the `Sized` trait are not permitted"
-        )
-        .span_label(impl_header_span, "impl of `Sized` not allowed")
-        .emit();
-        return;
-    }
-
-    if did == li.unsize_trait() {
-        struct_span_err!(
-            tcx.sess,
-            impl_header_span,
-            E0328,
-            "explicit impls for the `Unsize` trait are not permitted"
-        )
-        .span_label(impl_header_span, "impl of `Unsize` not allowed")
-        .emit();
-        return;
-    }
+            "explicit impls for the `{trait_name}` trait are not permitted"
+        );
+        err.span_label(impl_header_span, format!("impl of `{trait_name}` not allowed"));
+
+        // Maintain explicit error code for `Unsize`, since it has a useful
+        // explanation about using `CoerceUnsized` instead.
+        if Some(trait_def_id) == tcx.lang_items().unsize_trait() {
+            err.code(error_code!(E0328));
+        }
 
-    if tcx.features().unboxed_closures {
-        // the feature gate allows all Fn traits
+        err.emit();
         return;
     }
 
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 0e7a5ebf5ab..2f64a88f03a 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -291,18 +291,15 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
             match param.kind {
                 hir::GenericParamKind::Lifetime { .. } => {}
                 hir::GenericParamKind::Type { default: Some(_), .. } => {
-                    let def_id = self.tcx.hir().local_def_id(param.hir_id);
-                    self.tcx.ensure().type_of(def_id);
+                    self.tcx.ensure().type_of(param.def_id);
                 }
                 hir::GenericParamKind::Type { .. } => {}
                 hir::GenericParamKind::Const { default, .. } => {
-                    let def_id = self.tcx.hir().local_def_id(param.hir_id);
-                    self.tcx.ensure().type_of(def_id);
+                    self.tcx.ensure().type_of(param.def_id);
                     if let Some(default) = default {
-                        let default_def_id = self.tcx.hir().local_def_id(default.hir_id);
                         // need to store default and type of default
-                        self.tcx.ensure().type_of(default_def_id);
-                        self.tcx.ensure().const_param_default(def_id);
+                        self.tcx.ensure().type_of(default.def_id);
+                        self.tcx.ensure().const_param_default(param.def_id);
                     }
                 }
             }
@@ -311,9 +308,9 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
     }
 
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
-        if let hir::ExprKind::Closure { .. } = expr.kind {
-            let def_id = self.tcx.hir().local_def_id(expr.hir_id);
-            self.tcx.ensure().generics_of(def_id);
+        if let hir::ExprKind::Closure(closure) = expr.kind {
+            self.tcx.ensure().generics_of(closure.def_id);
+            self.tcx.ensure().codegen_fn_attrs(closure.def_id);
             // We do not call `type_of` for closures here as that
             // depends on typecheck and would therefore hide
             // any further errors in case one typeck fails.
@@ -586,8 +583,12 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
                 tcx.ensure().type_of(item.owner_id);
                 tcx.ensure().predicates_of(item.owner_id);
                 match item.kind {
-                    hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.owner_id),
+                    hir::ForeignItemKind::Fn(..) => {
+                        tcx.ensure().codegen_fn_attrs(item.owner_id);
+                        tcx.ensure().fn_sig(item.owner_id)
+                    }
                     hir::ForeignItemKind::Static(..) => {
+                        tcx.ensure().codegen_fn_attrs(item.owner_id);
                         let mut visitor = HirPlaceholderCollector::default();
                         visitor.visit_foreign_item(item);
                         placeholder_type_error(
@@ -632,14 +633,12 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
             tcx.ensure().predicates_of(def_id);
 
             for f in struct_def.fields() {
-                let def_id = tcx.hir().local_def_id(f.hir_id);
-                tcx.ensure().generics_of(def_id);
-                tcx.ensure().type_of(def_id);
-                tcx.ensure().predicates_of(def_id);
+                tcx.ensure().generics_of(f.def_id);
+                tcx.ensure().type_of(f.def_id);
+                tcx.ensure().predicates_of(f.def_id);
             }
 
-            if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
-                let ctor_def_id = tcx.hir().local_def_id(ctor_hir_id);
+            if let Some(ctor_def_id) = struct_def.ctor_def_id() {
                 convert_variant_ctor(tcx, ctor_def_id);
             }
         }
@@ -676,6 +675,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
             tcx.ensure().type_of(def_id);
             tcx.ensure().predicates_of(def_id);
             tcx.ensure().fn_sig(def_id);
+            tcx.ensure().codegen_fn_attrs(def_id);
         }
     }
 }
@@ -687,6 +687,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
 
     match trait_item.kind {
         hir::TraitItemKind::Fn(..) => {
+            tcx.ensure().codegen_fn_attrs(def_id);
             tcx.ensure().type_of(def_id);
             tcx.ensure().fn_sig(def_id);
         }
@@ -736,6 +737,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
     let impl_item = tcx.hir().impl_item(impl_item_id);
     match impl_item.kind {
         hir::ImplItemKind::Fn(..) => {
+            tcx.ensure().codegen_fn_attrs(def_id);
             tcx.ensure().fn_sig(def_id);
         }
         hir::ImplItemKind::Type(_) => {
@@ -813,7 +815,6 @@ fn convert_variant(
         .fields()
         .iter()
         .map(|f| {
-            let fid = tcx.hir().local_def_id(f.hir_id);
             let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
             if let Some(prev_span) = dup_span {
                 tcx.sess.emit_err(errors::FieldAlreadyDeclared {
@@ -825,7 +826,11 @@ fn convert_variant(
                 seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
             }
 
-            ty::FieldDef { did: fid.to_def_id(), name: f.ident.name, vis: tcx.visibility(fid) }
+            ty::FieldDef {
+                did: f.def_id.to_def_id(),
+                name: f.ident.name,
+                vis: tcx.visibility(f.def_id),
+            }
         })
         .collect();
     let recovered = match def {
@@ -866,13 +871,9 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> {
                 .variants
                 .iter()
                 .map(|v| {
-                    let variant_did = Some(tcx.hir().local_def_id(v.id));
-                    let ctor_did =
-                        v.data.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
-
                     let discr = if let Some(ref e) = v.disr_expr {
                         distance_from_explicit = 0;
-                        ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id).to_def_id())
+                        ty::VariantDiscr::Explicit(e.def_id.to_def_id())
                     } else {
                         ty::VariantDiscr::Relative(distance_from_explicit)
                     };
@@ -880,8 +881,8 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> {
 
                     convert_variant(
                         tcx,
-                        variant_did,
-                        ctor_did,
+                        Some(v.def_id),
+                        v.data.ctor_def_id(),
                         v.ident,
                         discr,
                         &v.data,
@@ -894,13 +895,10 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> {
             (AdtKind::Enum, variants)
         }
         ItemKind::Struct(ref def, _) => {
-            let variant_did = None::<LocalDefId>;
-            let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
-
             let variants = std::iter::once(convert_variant(
                 tcx,
-                variant_did,
-                ctor_did,
+                None,
+                def.ctor_def_id(),
                 item.ident,
                 ty::VariantDiscr::Relative(0),
                 def,
@@ -912,13 +910,10 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> {
             (AdtKind::Struct, variants)
         }
         ItemKind::Union(ref def, _) => {
-            let variant_did = None;
-            let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
-
             let variants = std::iter::once(convert_variant(
                 tcx,
-                variant_did,
-                ctor_did,
+                None,
+                def.ctor_def_id(),
                 item.ident,
                 ty::VariantDiscr::Relative(0),
                 def,
@@ -1178,8 +1173,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
 
         Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => {
             let ty = tcx.type_of(tcx.hir().get_parent_item(hir_id));
-            let inputs =
-                data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id)));
+            let inputs = data.fields().iter().map(|f| tcx.type_of(f.def_id));
             ty::Binder::dummy(tcx.mk_fn_sig(
                 inputs,
                 ty,
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index c7777a94689..b369a1eb109 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -51,7 +51,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
                 // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
                 None
             } else if tcx.lazy_normalization() {
-                if let Some(param_id) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) {
+                if let Some(param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id) {
                     // If the def_id we are calling generics_of on is an anon ct default i.e:
                     //
                     // struct Foo<const N: usize = { .. }>;
@@ -77,8 +77,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
                     // This has some implications for how we get the predicates available to the anon const
                     // see `explicit_predicates_of` for more information on this
                     let generics = tcx.generics_of(parent_def_id.to_def_id());
-                    let param_def = tcx.hir().local_def_id(param_id).to_def_id();
-                    let param_def_idx = generics.param_def_id_to_index[&param_def];
+                    let param_def_idx = generics.param_def_id_to_index[&param_id.to_def_id()];
                     // In the above example this would be .params[..N#0]
                     let params = generics.params[..param_def_idx as usize].to_owned();
                     let param_def_id_to_index =
@@ -241,7 +240,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
     params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef {
         name: param.name.ident().name,
         index: own_start + i as u32,
-        def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
+        def_id: param.def_id.to_def_id(),
         pure_wrt_drop: param.pure_wrt_drop,
         kind: ty::GenericParamDefKind::Lifetime,
     }));
@@ -286,7 +285,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
             Some(ty::GenericParamDef {
                 index: next_index(),
                 name: param.name.ident().name,
-                def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
+                def_id: param.def_id.to_def_id(),
                 pure_wrt_drop: param.pure_wrt_drop,
                 kind,
             })
@@ -303,7 +302,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
             Some(ty::GenericParamDef {
                 index: next_index(),
                 name: param.name.ident().name,
-                def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
+                def_id: param.def_id.to_def_id(),
                 pure_wrt_drop: param.pure_wrt_drop,
                 kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
             })
diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
index 6ee7aa9cdac..ce5cde5b883 100644
--- a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
+++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
@@ -15,7 +15,6 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
 use rustc_middle::bug;
-use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_lifetime::*;
 use rustc_middle::ty::{self, DefIdTree, TyCtxt, TypeSuperVisitable, TypeVisitor};
@@ -25,9 +24,9 @@ use rustc_span::Span;
 use std::fmt;
 
 trait RegionExt {
-    fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
+    fn early(param: &GenericParam<'_>) -> (LocalDefId, Region);
 
-    fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
+    fn late(index: u32, param: &GenericParam<'_>) -> (LocalDefId, Region);
 
     fn id(&self) -> Option<DefId>;
 
@@ -35,20 +34,18 @@ trait RegionExt {
 }
 
 impl RegionExt for Region {
-    fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
-        let def_id = hir_map.local_def_id(param.hir_id);
-        debug!("Region::early: def_id={:?}", def_id);
-        (def_id, Region::EarlyBound(def_id.to_def_id()))
+    fn early(param: &GenericParam<'_>) -> (LocalDefId, Region) {
+        debug!("Region::early: def_id={:?}", param.def_id);
+        (param.def_id, Region::EarlyBound(param.def_id.to_def_id()))
     }
 
-    fn late(idx: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
+    fn late(idx: u32, param: &GenericParam<'_>) -> (LocalDefId, Region) {
         let depth = ty::INNERMOST;
-        let def_id = hir_map.local_def_id(param.hir_id);
         debug!(
             "Region::late: idx={:?}, param={:?} depth={:?} def_id={:?}",
-            idx, param, depth, def_id,
+            idx, param, depth, param.def_id,
         );
-        (def_id, Region::LateBound(depth, idx, def_id.to_def_id()))
+        (param.def_id, Region::LateBound(depth, idx, param.def_id.to_def_id()))
     }
 
     fn id(&self) -> Option<DefId> {
@@ -395,7 +392,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
                     .enumerate()
                     .map(|(late_bound_idx, param)| {
-                        let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+                        let pair = Region::late(late_bound_idx as u32, param);
                         let r = late_region_as_bound_region(self.tcx, &pair.1);
                         (pair, r)
                     })
@@ -492,7 +489,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 for param in generics.params {
                     match param.kind {
                         GenericParamKind::Lifetime { .. } => {
-                            let (def_id, reg) = Region::early(self.tcx.hir(), &param);
+                            let (def_id, reg) = Region::early(&param);
                             lifetimes.insert(def_id, reg);
                         }
                         GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {}
@@ -523,9 +520,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .params
                     .iter()
                     .filter_map(|param| match param.kind {
-                        GenericParamKind::Lifetime { .. } => {
-                            Some(Region::early(self.tcx.hir(), param))
-                        }
+                        GenericParamKind::Lifetime { .. } => Some(Region::early(param)),
                         GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
                     })
                     .collect();
@@ -573,7 +568,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
                     .enumerate()
                     .map(|(late_bound_idx, param)| {
-                        let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+                        let pair = Region::late(late_bound_idx as u32, param);
                         let r = late_region_as_bound_region(self.tcx, &pair.1);
                         (pair, r)
                     })
@@ -731,9 +726,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .params
                     .iter()
                     .filter_map(|param| match param.kind {
-                        GenericParamKind::Lifetime { .. } => {
-                            Some(Region::early(self.tcx.hir(), param))
-                        }
+                        GenericParamKind::Lifetime { .. } => Some(Region::early(param)),
                         GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
                     })
                     .collect();
@@ -779,9 +772,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     .params
                     .iter()
                     .filter_map(|param| match param.kind {
-                        GenericParamKind::Lifetime { .. } => {
-                            Some(Region::early(self.tcx.hir(), param))
-                        }
+                        GenericParamKind::Lifetime { .. } => Some(Region::early(param)),
                         GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None,
                     })
                     .collect();
@@ -886,7 +877,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                                 })
                                 .enumerate()
                                 .map(|(late_bound_idx, param)| {
-                                        Region::late(late_bound_idx as u32, this.tcx.hir(), param)
+                                        Region::late(late_bound_idx as u32, param)
                                 })
                                 .collect();
                         let binders: Vec<_> =
@@ -999,8 +990,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
             .enumerate()
             .map(|(late_bound_idx, param)| {
-                let pair =
-                    Region::late(initial_bound_vars + late_bound_idx as u32, self.tcx.hir(), param);
+                let pair = Region::late(initial_bound_vars + late_bound_idx as u32, param);
                 let r = late_region_as_bound_region(self.tcx, &pair.1);
                 lifetimes.insert(pair.0, pair.1);
                 r
@@ -1131,9 +1121,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     if self.tcx.is_late_bound(param.hir_id) {
                         let late_bound_idx = named_late_bound_vars;
                         named_late_bound_vars += 1;
-                        Some(Region::late(late_bound_idx, self.tcx.hir(), param))
+                        Some(Region::late(late_bound_idx, param))
                     } else {
-                        Some(Region::early(self.tcx.hir(), param))
+                        Some(Region::early(param))
                     }
                 }
                 GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
@@ -1149,7 +1139,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             })
             .enumerate()
             .map(|(late_bound_idx, param)| {
-                let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+                let pair = Region::late(late_bound_idx as u32, param);
                 late_region_as_bound_region(self.tcx, &pair.1)
             })
             .collect();
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 5d1ca1cbd23..e2da580de0c 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -199,7 +199,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
                     &icx,
                     &mut bounds,
                     &[],
-                    Some((param.hir_id, ast_generics.predicates)),
+                    Some((param.def_id, ast_generics.predicates)),
                     param.span,
                 );
                 trace!(?bounds);
@@ -316,10 +316,9 @@ fn const_evaluatable_predicates_of<'tcx>(
 
     impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
         fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
-            let def_id = self.tcx.hir().local_def_id(c.hir_id);
-            let ct = ty::Const::from_anon_const(self.tcx, def_id);
+            let ct = ty::Const::from_anon_const(self.tcx, c.def_id);
             if let ty::ConstKind::Unevaluated(_) = ct.kind() {
-                let span = self.tcx.hir().span(c.hir_id);
+                let span = self.tcx.def_span(c.def_id);
                 self.preds.insert((
                     ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct))
                         .to_predicate(self.tcx),
@@ -429,7 +428,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
             let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
             let parent_def_id = tcx.hir().get_parent_item(hir_id);
 
-            if tcx.hir().opt_const_param_default_param_hir_id(hir_id).is_some() {
+            if tcx.hir().opt_const_param_default_param_def_id(hir_id).is_some() {
                 // In `generics_of` we set the generics' parent to be our parent's parent which means that
                 // we lose out on the predicates of our actual parent if we dont return those predicates here.
                 // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
@@ -531,7 +530,7 @@ pub(super) fn super_predicates_that_define_assoc_type(
         let is_trait_alias = tcx.is_trait_alias(trait_def_id);
         let superbounds2 = icx.type_parameter_bounds_in_generics(
             generics,
-            item.hir_id(),
+            item.owner_id.def_id,
             self_param_ty,
             OnlySelfBounds(!is_trait_alias),
             assoc_name,
@@ -641,7 +640,7 @@ pub(super) fn type_param_predicates(
     let extra_predicates = extend.into_iter().chain(
         icx.type_parameter_bounds_in_generics(
             ast_generics,
-            param_id,
+            def_id,
             ty,
             OnlySelfBounds(true),
             Some(assoc_name),
@@ -666,13 +665,11 @@ impl<'tcx> ItemCtxt<'tcx> {
     fn type_parameter_bounds_in_generics(
         &self,
         ast_generics: &'tcx hir::Generics<'tcx>,
-        param_id: hir::HirId,
+        param_def_id: LocalDefId,
         ty: Ty<'tcx>,
         only_self_bounds: OnlySelfBounds,
         assoc_name: Option<Ident>,
     ) -> Vec<(ty::Predicate<'tcx>, Span)> {
-        let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id();
-        trace!(?param_def_id);
         ast_generics
             .predicates
             .iter()
@@ -681,7 +678,7 @@ impl<'tcx> ItemCtxt<'tcx> {
                 _ => None,
             })
             .flat_map(|bp| {
-                let bt = if bp.is_param_bound(param_def_id) {
+                let bt = if bp.is_param_bound(param_def_id.to_def_id()) {
                     Some(ty)
                 } else if !only_self_bounds.0 {
                     Some(self.to_ty(bp.bounded_ty))
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 2402495c2e4..9bd1715ce39 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -514,10 +514,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
                 }
 
                 Node::GenericParam(&GenericParam {
-                    hir_id: param_hir_id,
+                    def_id: param_def_id,
                     kind: GenericParamKind::Const { default: Some(ct), .. },
                     ..
-                }) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)),
+                }) if ct.hir_id == hir_id => tcx.type_of(param_def_id),
 
                 x => tcx.ty_error_with_message(
                     DUMMY_SP,
@@ -636,9 +636,8 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
             self.tcx.hir()
         }
         fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-            if let hir::ExprKind::Closure { .. } = ex.kind {
-                let def_id = self.tcx.hir().local_def_id(ex.hir_id);
-                self.check(def_id);
+            if let hir::ExprKind::Closure(closure) = ex.kind {
+                self.check(closure.def_id);
             }
             intravisit::walk_expr(self, ex);
         }
@@ -771,9 +770,8 @@ fn find_opaque_ty_constraints_for_rpit(
             self.tcx.hir()
         }
         fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-            if let hir::ExprKind::Closure { .. } = ex.kind {
-                let def_id = self.tcx.hir().local_def_id(ex.hir_id);
-                self.check(def_id);
+            if let hir::ExprKind::Closure(closure) = ex.kind {
+                self.check(closure.def_id);
             }
             intravisit::walk_expr(self, ex);
         }
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index e50c267659e..ea0c2a20de3 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -22,7 +22,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate
 
     if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
     {
-        if tcx.hir().opt_const_param_default_param_hir_id(id).is_some() {
+        if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
             // In `generics_of` we set the generics' parent to be our parent's parent which means that
             // we lose out on the predicates of our actual parent if we dont return those predicates here.
             // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index da27554a229..d70ec94f5b6 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -754,7 +754,7 @@ impl<'a> State<'a> {
         for v in variants {
             self.space_if_not_bol();
             self.maybe_print_comment(v.span.lo());
-            self.print_outer_attributes(self.attrs(v.id));
+            self.print_outer_attributes(self.attrs(v.hir_id));
             self.ibox(INDENT_UNIT);
             self.print_variant(v);
             self.word(",");
@@ -1481,6 +1481,7 @@ impl<'a> State<'a> {
                 body,
                 fn_decl_span: _,
                 movability: _,
+                def_id: _,
             }) => {
                 self.print_closure_binder(binder, bound_generic_params);
                 self.print_capture_clause(capture_clause);
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 2b019c8c9b7..ed2218b8746 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -504,7 +504,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // method lookup.
             let Ok(pick) = self
             .probe_for_name(
-                call_expr.span,
                 Mode::MethodCall,
                 segment.ident,
                 IsSuggestion(true),
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 3c57e33f6f7..1578cddd490 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -26,10 +26,12 @@ pub(super) fn check_fn<'a, 'tcx>(
     param_env: ty::ParamEnv<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
     decl: &'tcx hir::FnDecl<'tcx>,
-    fn_id: hir::HirId,
+    fn_def_id: LocalDefId,
     body: &'tcx hir::Body<'tcx>,
     can_be_generator: Option<hir::Movability>,
 ) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
+    let fn_id = inherited.tcx.hir().local_def_id_to_hir_id(fn_def_id);
+
     // Create the function context. This is either derived from scratch or,
     // in the case of closures, based on the outer context.
     let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id);
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 3001e799476..6cf9e23b40b 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -4,7 +4,7 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes};
 
 use hir::def::DefKind;
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::LocalDefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir_analysis::astconv::AstConv;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -35,24 +35,20 @@ struct ClosureSignatures<'tcx> {
     bound_sig: ty::PolyFnSig<'tcx>,
     /// The signature within the function body.
     /// This mostly differs in the sense that lifetimes are now early bound and any
-    /// opaque types from the signature expectation are overriden in case there are
+    /// opaque types from the signature expectation are overridden in case there are
     /// explicit hidden types written by the user in the closure signature.
     liberated_sig: ty::FnSig<'tcx>,
 }
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")]
+    #[instrument(skip(self, closure), level = "debug")]
     pub fn check_expr_closure(
         &self,
-        expr: &hir::Expr<'_>,
-        _capture: hir::CaptureBy,
-        decl: &'tcx hir::FnDecl<'tcx>,
-        body_id: hir::BodyId,
-        gen: Option<hir::Movability>,
+        closure: &hir::Closure<'tcx>,
+        expr_span: Span,
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
-        trace!("decl = {:#?}", decl);
-        trace!("expr = {:#?}", expr);
+        trace!("decl = {:#?}", closure.fn_decl);
 
         // It's always helpful for inference if we know the kind of
         // closure sooner rather than later, so first examine the expected
@@ -61,26 +57,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Some(ty) => self.deduce_expectations_from_expected_type(ty),
             None => (None, None),
         };
-        let body = self.tcx.hir().body(body_id);
-        self.check_closure(expr, expected_kind, decl, body, gen, expected_sig)
+        let body = self.tcx.hir().body(closure.body);
+        self.check_closure(closure, expr_span, expected_kind, body, expected_sig)
     }
 
-    #[instrument(skip(self, expr, body, decl), level = "debug", ret)]
+    #[instrument(skip(self, closure, body), level = "debug", ret)]
     fn check_closure(
         &self,
-        expr: &hir::Expr<'_>,
+        closure: &hir::Closure<'tcx>,
+        expr_span: Span,
         opt_kind: Option<ty::ClosureKind>,
-        decl: &'tcx hir::FnDecl<'tcx>,
         body: &'tcx hir::Body<'tcx>,
-        gen: Option<hir::Movability>,
         expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> Ty<'tcx> {
-        trace!("decl = {:#?}", decl);
-        let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id);
+        trace!("decl = {:#?}", closure.fn_decl);
+        let expr_def_id = closure.def_id;
         debug!(?expr_def_id);
 
         let ClosureSignatures { bound_sig, liberated_sig } =
-            self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig);
+            self.sig_of_closure(expr_def_id, closure.fn_decl, body, expected_sig);
 
         debug!(?bound_sig, ?liberated_sig);
 
@@ -88,10 +83,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self,
             self.param_env.without_const(),
             liberated_sig,
-            decl,
-            expr.hir_id,
+            closure.fn_decl,
+            expr_def_id,
             body,
-            gen,
+            closure.movability,
         )
         .1;
 
@@ -102,7 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin {
             kind: TypeVariableOriginKind::ClosureSynthetic,
-            span: self.tcx.hir().span(expr.hir_id),
+            span: self.tcx.def_span(expr_def_id),
         });
 
         if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types
@@ -148,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             None => self.next_ty_var(TypeVariableOrigin {
                 // FIXME(eddyb) distinguish closure kind inference variables from the rest.
                 kind: TypeVariableOriginKind::ClosureSynthetic,
-                span: expr.span,
+                span: expr_span,
             }),
         };
 
@@ -173,34 +168,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
     ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
         match *expected_ty.kind() {
-            ty::Opaque(def_id, substs) => {
-                let bounds = self.tcx.bound_explicit_item_bounds(def_id);
-                let sig =
-                    bounds.subst_iter_copied(self.tcx, substs).find_map(|(pred, span)| match pred
-                        .kind()
-                        .skip_binder()
-                    {
-                        ty::PredicateKind::Projection(proj_predicate) => self
-                            .deduce_sig_from_projection(
-                                Some(span),
-                                pred.kind().rebind(proj_predicate),
-                            ),
-                        _ => None,
-                    });
-
-                let kind = bounds
-                    .0
-                    .iter()
-                    .filter_map(|(pred, _)| match pred.kind().skip_binder() {
-                        ty::PredicateKind::Trait(tp) => {
-                            self.tcx.fn_trait_kind_from_lang_item(tp.def_id())
-                        }
-                        _ => None,
-                    })
-                    .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
-                trace!(?sig, ?kind);
-                (sig, kind)
-            }
+            ty::Opaque(def_id, substs) => self.deduce_signature_from_predicates(
+                self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
+            ),
             ty::Dynamic(ref object_type, ..) => {
                 let sig = object_type.projection_bounds().find_map(|pb| {
                     let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self);
@@ -211,7 +181,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did));
                 (sig, kind)
             }
-            ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid),
+            ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates(
+                self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
+            ),
             ty::FnPtr(sig) => {
                 let expected_sig = ExpectedSig { cause_span: None, sig };
                 (Some(expected_sig), Some(ty::ClosureKind::Fn))
@@ -220,19 +192,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn deduce_expectations_from_obligations(
+    fn deduce_signature_from_predicates(
         &self,
-        expected_vid: ty::TyVid,
+        predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>,
     ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
         let mut expected_sig = None;
         let mut expected_kind = None;
 
-        for obligation in traits::elaborate_obligations(
+        for obligation in traits::elaborate_predicates_with_span(
             self.tcx,
             // Reverse the obligations here, since `elaborate_*` uses a stack,
             // and we want to keep inference generally in the same order of
             // the registered obligations.
-            self.obligations_for_self_ty(expected_vid).rev().collect(),
+            predicates.rev(),
         ) {
             debug!(?obligation.predicate);
             let bound_predicate = obligation.predicate.kind();
@@ -342,30 +314,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn sig_of_closure(
         &self,
-        hir_id: hir::HirId,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
         body: &hir::Body<'_>,
         expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> ClosureSignatures<'tcx> {
         if let Some(e) = expected_sig {
-            self.sig_of_closure_with_expectation(hir_id, expr_def_id, decl, body, e)
+            self.sig_of_closure_with_expectation(expr_def_id, decl, body, e)
         } else {
-            self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body)
+            self.sig_of_closure_no_expectation(expr_def_id, decl, body)
         }
     }
 
     /// If there is no expected signature, then we will convert the
     /// types that the user gave into a signature.
-    #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
+    #[instrument(skip(self, expr_def_id, decl, body), level = "debug")]
     fn sig_of_closure_no_expectation(
         &self,
-        hir_id: hir::HirId,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
         body: &hir::Body<'_>,
     ) -> ClosureSignatures<'tcx> {
-        let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body);
+        let bound_sig = self.supplied_sig_of_closure(expr_def_id, decl, body);
 
         self.closure_sigs(expr_def_id, body, bound_sig)
     }
@@ -411,17 +381,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// # Arguments
     ///
-    /// - `expr_def_id`: the `DefId` of the closure expression
+    /// - `expr_def_id`: the `LocalDefId` of the closure expression
     /// - `decl`: the HIR declaration of the closure
     /// - `body`: the body of the closure
     /// - `expected_sig`: the expected signature (if any). Note that
     ///   this is missing a binder: that is, there may be late-bound
     ///   regions with depth 1, which are bound then by the closure.
-    #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")]
+    #[instrument(skip(self, expr_def_id, decl, body), level = "debug")]
     fn sig_of_closure_with_expectation(
         &self,
-        hir_id: hir::HirId,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
         body: &hir::Body<'_>,
         expected_sig: ExpectedSig<'tcx>,
@@ -430,7 +399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // expectation if things don't see to match up with what we
         // expect.
         if expected_sig.sig.c_variadic() != decl.c_variadic {
-            return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body);
+            return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
         } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 {
             return self.sig_of_closure_with_mismatched_number_of_arguments(
                 expr_def_id,
@@ -466,27 +435,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Along the way, it also writes out entries for types that the user
         // wrote into our typeck results, which are then later used by the privacy
         // check.
-        match self.merge_supplied_sig_with_expectation(
-            hir_id,
-            expr_def_id,
-            decl,
-            body,
-            closure_sigs,
-        ) {
+        match self.merge_supplied_sig_with_expectation(expr_def_id, decl, body, closure_sigs) {
             Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok),
-            Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body),
+            Err(_) => self.sig_of_closure_no_expectation(expr_def_id, decl, body),
         }
     }
 
     fn sig_of_closure_with_mismatched_number_of_arguments(
         &self,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
         body: &hir::Body<'_>,
         expected_sig: ExpectedSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
         let hir = self.tcx.hir();
-        let expr_map_node = hir.get_if_local(expr_def_id).unwrap();
+        let expr_map_node = hir.get_by_def_id(expr_def_id);
         let expected_args: Vec<_> = expected_sig
             .sig
             .skip_binder()
@@ -499,7 +462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             None => (None, Vec::new()),
         };
         let expected_span =
-            expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap());
+            expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id));
         self.report_arg_count_mismatch(
             expected_span,
             closure_span,
@@ -517,11 +480,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Enforce the user's types against the expectation. See
     /// `sig_of_closure_with_expectation` for details on the overall
     /// strategy.
-    #[instrument(level = "debug", skip(self, hir_id, expr_def_id, decl, body, expected_sigs))]
+    #[instrument(level = "debug", skip(self, expr_def_id, decl, body, expected_sigs))]
     fn merge_supplied_sig_with_expectation(
         &self,
-        hir_id: hir::HirId,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
         body: &hir::Body<'_>,
         mut expected_sigs: ClosureSignatures<'tcx>,
@@ -530,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         //
         // (See comment on `sig_of_closure_with_expectation` for the
         // meaning of these letters.)
-        let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body);
+        let supplied_sig = self.supplied_sig_of_closure(expr_def_id, decl, body);
 
         debug!(?supplied_sig);
 
@@ -610,8 +572,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(skip(self, decl, body), level = "debug", ret)]
     fn supplied_sig_of_closure(
         &self,
-        hir_id: hir::HirId,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         decl: &hir::FnDecl<'_>,
         body: &hir::Body<'_>,
     ) -> ty::PolyFnSig<'tcx> {
@@ -620,6 +581,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         trace!("decl = {:#?}", decl);
         debug!(?body.generator_kind);
 
+        let hir_id = self.tcx.hir().local_def_id_to_hir_id(expr_def_id);
         let bound_vars = self.tcx.late_bound_vars(hir_id);
 
         // First, convert the types that the user supplied (if any).
@@ -678,7 +640,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(skip(self), level = "debug", ret)]
     fn deduce_future_output_from_obligations(
         &self,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         body_id: hir::HirId,
     ) -> Option<Ty<'tcx>> {
         let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
@@ -827,14 +789,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn closure_sigs(
         &self,
-        expr_def_id: DefId,
+        expr_def_id: LocalDefId,
         body: &hir::Body<'_>,
         bound_sig: ty::PolyFnSig<'tcx>,
     ) -> ClosureSignatures<'tcx> {
-        let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, bound_sig);
+        let liberated_sig =
+            self.tcx().liberate_late_bound_regions(expr_def_id.to_def_id(), bound_sig);
         let liberated_sig = self.inh.normalize_associated_types_in(
             body.value.span,
-            body.value.hir_id,
+            self.tcx.hir().local_def_id_to_hir_id(expr_def_id),
             self.param_env,
             liberated_sig,
         );
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index e948d832e32..13a03b33de8 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -30,7 +30,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{Closure, ExprKind, HirId, QPath};
+use rustc_hir::{ExprKind, HirId, QPath};
 use rustc_hir_analysis::astconv::AstConv as _;
 use rustc_hir_analysis::check::ty_kind_suggestion;
 use rustc_infer::infer;
@@ -324,9 +324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ExprKind::Match(discrim, arms, match_src) => {
                 self.check_match(expr, &discrim, arms, expected, match_src)
             }
-            ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => {
-                self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected)
-            }
+            ExprKind::Closure(closure) => self.check_expr_closure(closure, expr.span, expected),
             ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected),
             ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected),
             ExprKind::MethodCall(segment, receiver, args, _) => {
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index fce2a5888ba..275f7d12148 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -352,8 +352,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                 self.consume_expr(base);
             }
 
-            hir::ExprKind::Closure { .. } => {
-                self.walk_captures(expr);
+            hir::ExprKind::Closure(closure) => {
+                self.walk_captures(closure);
             }
 
             hir::ExprKind::Box(ref base) => {
@@ -745,7 +745,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
     ///
     /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
     /// closure as the DefId.
-    fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) {
+    fn walk_captures(&mut self, closure_expr: &hir::Closure<'_>) {
         fn upvar_is_local_variable<'tcx>(
             upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
             upvar_id: hir::HirId,
@@ -757,7 +757,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
         debug!("walk_captures({:?})", closure_expr);
 
         let tcx = self.tcx();
-        let closure_def_id = tcx.hir().local_def_id(closure_expr.hir_id);
+        let closure_def_id = closure_expr.def_id;
         let upvars = tcx.upvars_mentioned(self.body_owner);
 
         // For purposes of this function, generator and closures are equivalent.
@@ -829,10 +829,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                         // be a local variable
                         PlaceBase::Local(*var_hir_id)
                     };
+                    let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id);
                     let place_with_id = PlaceWithHirId::new(
-                        capture_info.path_expr_id.unwrap_or(
-                            capture_info.capture_kind_expr_id.unwrap_or(closure_expr.hir_id),
-                        ),
+                        capture_info
+                            .path_expr_id
+                            .unwrap_or(capture_info.capture_kind_expr_id.unwrap_or(closure_hir_id)),
                         place.base_ty,
                         place_base,
                         place.projections.clone(),
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index c6bd771fad2..97d05b4f95c 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -488,9 +488,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match length {
             &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span),
             hir::ArrayLen::Body(anon_const) => {
-                let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
-                let span = self.tcx.hir().span(anon_const.hir_id);
-                let c = ty::Const::from_anon_const(self.tcx, const_def_id);
+                let span = self.tcx.def_span(anon_const.def_id);
+                let c = ty::Const::from_anon_const(self.tcx, anon_const.def_id);
                 self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None));
                 self.normalize_associated_types_in(span, c)
             }
@@ -502,10 +501,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ast_c: &hir::AnonConst,
         param_def_id: DefId,
     ) -> ty::Const<'tcx> {
-        let const_def = ty::WithOptConstParam {
-            did: self.tcx.hir().local_def_id(ast_c.hir_id),
-            const_param_did: Some(param_def_id),
-        };
+        let const_def =
+            ty::WithOptConstParam { did: ast_c.def_id, const_param_did: Some(param_def_id) };
         let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def);
         self.register_wf_obligation(
             c.into(),
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 183e80f2e08..6fd609aeaa0 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -251,7 +251,7 @@ fn typeck_with_fallback<'tcx>(
                 param_env,
                 fn_sig,
             );
-            check_fn(&inh, param_env, fn_sig, decl, id, body, None).0
+            check_fn(&inh, param_env, fn_sig, decl, def_id, body, None).0
         } else {
             let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
             let expected_type = body_ty
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index 4a8b7749365..3f390cba3e7 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -93,17 +93,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         call_expr_id: hir::HirId,
         allow_private: bool,
     ) -> bool {
-        let mode = probe::Mode::MethodCall;
         match self.probe_for_name(
-            method_name.span,
-            mode,
+            probe::Mode::MethodCall,
             method_name,
             IsSuggestion(false),
             self_ty,
             call_expr_id,
             ProbeScope::TraitsInScope,
         ) {
-            Ok(..) => true,
+            Ok(pick) => {
+                pick.maybe_emit_unstable_name_collision_hint(
+                    self.tcx,
+                    method_name.span,
+                    call_expr_id,
+                );
+                true
+            }
             Err(NoMatch(..)) => false,
             Err(Ambiguity(..)) => true,
             Err(PrivateMatch(..)) => allow_private,
@@ -125,10 +130,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         let params = self
             .probe_for_name(
-                method_name.span,
                 probe::Mode::MethodCall,
                 method_name,
-                IsSuggestion(false),
+                IsSuggestion(true),
                 self_ty,
                 call_expr.hir_id,
                 ProbeScope::TraitsInScope,
@@ -175,7 +179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         args: &'tcx [hir::Expr<'tcx>],
     ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
         let pick =
-            self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
+            self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
 
         self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
 
@@ -200,13 +204,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() });
                 // We probe again to see if there might be a borrow mutability discrepancy.
                 match self.lookup_probe(
-                    span,
                     segment.ident,
                     trait_type,
                     call_expr,
                     ProbeScope::TraitsInScope,
                 ) {
-                    Ok(ref new_pick) if *new_pick != pick => {
+                    Ok(ref new_pick) if pick.differs_from(new_pick) => {
                         needs_mut = true;
                     }
                     _ => {}
@@ -214,28 +217,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
 
             // We probe again, taking all traits into account (not only those in scope).
-            let mut candidates = match self.lookup_probe(
-                span,
-                segment.ident,
-                self_ty,
-                call_expr,
-                ProbeScope::AllTraits,
-            ) {
-                // If we find a different result the caller probably forgot to import a trait.
-                Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)],
-                Err(Ambiguity(ref sources)) => sources
-                    .iter()
-                    .filter_map(|source| {
-                        match *source {
-                            // Note: this cannot come from an inherent impl,
-                            // because the first probing succeeded.
-                            CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
-                            CandidateSource::Trait(_) => None,
-                        }
-                    })
-                    .collect(),
-                _ => Vec::new(),
-            };
+            let mut candidates =
+                match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) {
+                    // If we find a different result the caller probably forgot to import a trait.
+                    Ok(ref new_pick) if pick.differs_from(new_pick) => {
+                        vec![new_pick.item.container_id(self.tcx)]
+                    }
+                    Err(Ambiguity(ref sources)) => sources
+                        .iter()
+                        .filter_map(|source| {
+                            match *source {
+                                // Note: this cannot come from an inherent impl,
+                                // because the first probing succeeded.
+                                CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
+                                CandidateSource::Trait(_) => None,
+                            }
+                        })
+                        .collect(),
+                    _ => Vec::new(),
+                };
             candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id));
 
             return Err(IllegalSizedBound(candidates, needs_mut, span));
@@ -247,23 +247,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self, call_expr))]
     pub fn lookup_probe(
         &self,
-        span: Span,
         method_name: Ident,
         self_ty: Ty<'tcx>,
         call_expr: &'tcx hir::Expr<'tcx>,
         scope: ProbeScope,
     ) -> probe::PickResult<'tcx> {
-        let mode = probe::Mode::MethodCall;
-        let self_ty = self.resolve_vars_if_possible(self_ty);
-        self.probe_for_name(
-            span,
-            mode,
+        let pick = self.probe_for_name(
+            probe::Mode::MethodCall,
             method_name,
             IsSuggestion(false),
             self_ty,
             call_expr.hir_id,
             scope,
-        )
+        )?;
+        pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id);
+        Ok(pick)
     }
 
     pub(super) fn obligation_for_method(
@@ -587,7 +585,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let pick = self.probe_for_name(
-            span,
             probe::Mode::Path,
             method_name,
             IsSuggestion(false),
@@ -596,6 +593,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ProbeScope::TraitsInScope,
         )?;
 
+        pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id);
+
         self.lint_fully_qualified_call_from_2018(
             span,
             method_name,
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3fcd073f597..46a76085189 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -17,6 +17,7 @@ use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
+use rustc_middle::ty::AssocItem;
 use rustc_middle::ty::GenericParamDefKind;
 use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable};
 use rustc_middle::ty::{InternalSubsts, SubstsRef};
@@ -83,8 +84,6 @@ struct ProbeContext<'a, 'tcx> {
     unsatisfied_predicates:
         Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
 
-    is_suggestion: IsSuggestion,
-
     scope_expr_id: hir::HirId,
 }
 
@@ -193,7 +192,7 @@ impl AutorefOrPtrAdjustment {
     }
 }
 
-#[derive(Debug, PartialEq, Clone)]
+#[derive(Debug, Clone)]
 pub struct Pick<'tcx> {
     pub item: ty::AssocItem,
     pub kind: PickKind<'tcx>,
@@ -209,6 +208,9 @@ pub struct Pick<'tcx> {
     /// `*mut T`, convert it to `*const T`.
     pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>,
     pub self_ty: Ty<'tcx>,
+
+    /// Unstable candidates alongside the stable ones.
+    unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -298,7 +300,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self))]
     pub fn probe_for_name(
         &self,
-        span: Span,
         mode: Mode,
         item_name: Ident,
         is_suggestion: IsSuggestion,
@@ -307,7 +308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         scope: ProbeScope,
     ) -> PickResult<'tcx> {
         self.probe_op(
-            span,
+            item_name.span,
             mode,
             Some(item_name),
             None,
@@ -446,7 +447,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return_type,
                 orig_values,
                 steps.steps,
-                is_suggestion,
                 scope_expr_id,
             );
 
@@ -541,7 +541,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         return_type: Option<Ty<'tcx>>,
         orig_steps_var_values: OriginalQueryValues<'tcx>,
         steps: &'tcx [CandidateStep<'tcx>],
-        is_suggestion: IsSuggestion,
         scope_expr_id: hir::HirId,
     ) -> ProbeContext<'a, 'tcx> {
         ProbeContext {
@@ -559,7 +558,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             allow_similar_names: false,
             private_candidate: None,
             unsatisfied_predicates: Vec::new(),
-            is_suggestion,
             scope_expr_id,
         }
     }
@@ -881,7 +879,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
-    pub fn matches_return_type(
+    fn matches_return_type(
         &self,
         method: &ty::AssocItem,
         self_ty: Option<Ty<'tcx>>,
@@ -1052,26 +1050,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
     }
 
     fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
-        let mut unstable_candidates = Vec::new();
-        let pick = self.pick_all_method(Some(&mut unstable_candidates));
+        let pick = self.pick_all_method(Some(&mut vec![]));
 
         // In this case unstable picking is done by `pick_method`.
         if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable {
             return pick;
         }
 
-        match pick {
-            // Emit a lint if there are unstable candidates alongside the stable ones.
-            //
-            // We suppress warning if we're picking the method only because it is a
-            // suggestion.
-            Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => {
-                self.emit_unstable_name_collision_hint(p, &unstable_candidates);
-                pick
-            }
-            Some(_) => pick,
-            None => self.pick_all_method(None),
+        if pick.is_none() {
+            return self.pick_all_method(None);
         }
+        pick
     }
 
     fn pick_all_method(
@@ -1216,7 +1205,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty));
 
         let mut possibly_unsatisfied_predicates = Vec::new();
-        let mut unstable_candidates = Vec::new();
 
         for (kind, candidates) in
             &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
@@ -1226,26 +1214,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 self_ty,
                 candidates.iter(),
                 &mut possibly_unsatisfied_predicates,
-                Some(&mut unstable_candidates),
+                Some(&mut vec![]),
             );
-            if let Some(pick) = res {
-                if !self.is_suggestion.0 && !unstable_candidates.is_empty() {
-                    if let Ok(p) = &pick {
-                        // Emit a lint if there are unstable candidates alongside the stable ones.
-                        //
-                        // We suppress warning if we're picking the method only because it is a
-                        // suggestion.
-                        self.emit_unstable_name_collision_hint(p, &unstable_candidates);
-                    }
-                }
-                return Some(pick);
+            if res.is_some() {
+                return res;
             }
         }
 
         debug!("searching unstable candidates");
         let res = self.consider_candidates(
             self_ty,
-            unstable_candidates.iter().map(|(c, _)| c),
+            self.inherent_candidates.iter().chain(&self.extension_candidates),
             &mut possibly_unsatisfied_predicates,
             None,
         );
@@ -1300,7 +1279,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             Option<ty::Predicate<'tcx>>,
             Option<ObligationCause<'tcx>>,
         )>,
-        unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+        mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
     ) -> Option<PickResult<'tcx>>
     where
         ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone,
@@ -1324,7 +1303,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             }
         }
 
-        if let Some(uc) = unstable_candidates {
+        if let Some(uc) = &mut unstable_candidates {
             applicable_candidates.retain(|&(p, _)| {
                 if let stability::EvalResult::Deny { feature, .. } =
                     self.tcx.eval_stability(p.item.def_id, None, self.span, None)
@@ -1343,30 +1322,63 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
 
         applicable_candidates.pop().map(|(probe, status)| {
             if status == ProbeResult::Match {
-                Ok(probe.to_unadjusted_pick(self_ty))
+                Ok(probe
+                    .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default()))
             } else {
                 Err(MethodError::BadReturnType)
             }
         })
     }
+}
+
+impl<'tcx> Pick<'tcx> {
+    /// In case there were unstable name collisions, emit them as a lint.
+    /// Checks whether two picks do not refer to the same trait item for the same `Self` type.
+    /// Only useful for comparisons of picks in order to improve diagnostics.
+    /// Do not use for type checking.
+    pub fn differs_from(&self, other: &Self) -> bool {
+        let Self {
+            item:
+                AssocItem {
+                    def_id,
+                    name: _,
+                    kind: _,
+                    container: _,
+                    trait_item_def_id: _,
+                    fn_has_self_parameter: _,
+                },
+            kind: _,
+            import_ids: _,
+            autoderefs: _,
+            autoref_or_ptr_adjustment: _,
+            self_ty,
+            unstable_candidates: _,
+        } = *self;
+        self_ty != other.self_ty || def_id != other.item.def_id
+    }
 
-    fn emit_unstable_name_collision_hint(
+    /// In case there were unstable name collisions, emit them as a lint.
+    pub fn maybe_emit_unstable_name_collision_hint(
         &self,
-        stable_pick: &Pick<'_>,
-        unstable_candidates: &[(Candidate<'tcx>, Symbol)],
+        tcx: TyCtxt<'tcx>,
+        span: Span,
+        scope_expr_id: hir::HirId,
     ) {
-        let def_kind = stable_pick.item.kind.as_def_kind();
-        self.tcx.struct_span_lint_hir(
+        if self.unstable_candidates.is_empty() {
+            return;
+        }
+        let def_kind = self.item.kind.as_def_kind();
+        tcx.struct_span_lint_hir(
             lint::builtin::UNSTABLE_NAME_COLLISIONS,
-            self.scope_expr_id,
-            self.span,
+            scope_expr_id,
+            span,
             format!(
                 "{} {} with this name may be added to the standard library in the future",
                 def_kind.article(),
-                def_kind.descr(stable_pick.item.def_id),
+                def_kind.descr(self.item.def_id),
             ),
             |lint| {
-                match (stable_pick.item.kind, stable_pick.item.container) {
+                match (self.item.kind, self.item.container) {
                     (ty::AssocKind::Fn, _) => {
                         // FIXME: This should be a `span_suggestion` instead of `help`
                         // However `self.span` only
@@ -1375,31 +1387,31 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                         lint.help(&format!(
                             "call with fully qualified syntax `{}(...)` to keep using the current \
                              method",
-                            self.tcx.def_path_str(stable_pick.item.def_id),
+                            tcx.def_path_str(self.item.def_id),
                         ));
                     }
                     (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => {
-                        let def_id = stable_pick.item.container_id(self.tcx);
+                        let def_id = self.item.container_id(tcx);
                         lint.span_suggestion(
-                            self.span,
+                            span,
                             "use the fully qualified path to the associated const",
                             format!(
                                 "<{} as {}>::{}",
-                                stable_pick.self_ty,
-                                self.tcx.def_path_str(def_id),
-                                stable_pick.item.name
+                                self.self_ty,
+                                tcx.def_path_str(def_id),
+                                self.item.name
                             ),
                             Applicability::MachineApplicable,
                         );
                     }
                     _ => {}
                 }
-                if self.tcx.sess.is_nightly_build() {
-                    for (candidate, feature) in unstable_candidates {
+                if tcx.sess.is_nightly_build() {
+                    for (candidate, feature) in &self.unstable_candidates {
                         lint.help(&format!(
                             "add `#![feature({})]` to the crate attributes to enable `{}`",
                             feature,
-                            self.tcx.def_path_str(candidate.item.def_id),
+                            tcx.def_path_str(candidate.item.def_id),
                         ));
                     }
                 }
@@ -1408,7 +1420,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             },
         );
     }
+}
 
+impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
     fn select_trait_candidate(
         &self,
         trait_ref: ty::TraitRef<'tcx>,
@@ -1667,6 +1681,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             autoderefs: 0,
             autoref_or_ptr_adjustment: None,
             self_ty,
+            unstable_candidates: vec![],
         })
     }
 
@@ -1686,7 +1701,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 self.return_type,
                 self.orig_steps_var_values.clone(),
                 steps,
-                IsSuggestion(true),
                 self.scope_expr_id,
             );
             pcx.allow_similar_names = true;
@@ -1894,7 +1908,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
 }
 
 impl<'tcx> Candidate<'tcx> {
-    fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> {
+    fn to_unadjusted_pick(
+        &self,
+        self_ty: Ty<'tcx>,
+        unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
+    ) -> Pick<'tcx> {
         Pick {
             item: self.item,
             kind: match self.kind {
@@ -1919,6 +1937,7 @@ impl<'tcx> Candidate<'tcx> {
             autoderefs: 0,
             autoref_or_ptr_adjustment: None,
             self_ty,
+            unstable_candidates,
         }
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 43a5145b7e7..19f56c73823 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -20,11 +20,10 @@ use rustc_infer::infer::{
 };
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::traits::util::supertraits;
+use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
 use rustc_middle::ty::print::with_crate_prefix;
-use rustc_middle::ty::{
-    self, DefIdTree, GenericArg, GenericArgKind, ToPredicate, Ty, TyCtxt, TypeVisitable,
-};
+use rustc_middle::ty::{self, DefIdTree, GenericArgKind, ToPredicate, Ty, TyCtxt, TypeVisitable};
 use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Symbol;
@@ -263,15 +262,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }) => {
                 let tcx = self.tcx;
 
-                let actual = self.resolve_vars_if_possible(rcvr_ty);
-                let ty_str = self.ty_to_string(actual);
+                let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
+                let ty_str = self.ty_to_string(rcvr_ty);
                 let is_method = mode == Mode::MethodCall;
                 let item_kind = if is_method {
                     "method"
-                } else if actual.is_enum() {
+                } else if rcvr_ty.is_enum() {
                     "variant or associated item"
                 } else {
-                    match (item_name.as_str().chars().next(), actual.is_fresh_ty()) {
+                    match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) {
                         (Some(name), false) if name.is_lowercase() => "function or associated item",
                         (Some(_), false) => "associated item",
                         (Some(_), true) | (None, false) => "variant or associated item",
@@ -280,9 +279,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 };
 
                 if self.suggest_wrapping_range_with_parens(
-                    tcx, actual, source, span, item_name, &ty_str,
+                    tcx, rcvr_ty, source, span, item_name, &ty_str,
                 ) || self.suggest_constraining_numerical_ty(
-                    tcx, actual, source, span, item_kind, item_name, &ty_str,
+                    tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str,
                 ) {
                     return None;
                 }
@@ -290,9 +289,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // Don't show generic arguments when the method can't be found in any implementation (#81576).
                 let mut ty_str_reported = ty_str.clone();
-                if let ty::Adt(_, generics) = actual.kind() {
+                if let ty::Adt(_, generics) = rcvr_ty.kind() {
                     if generics.len() > 0 {
-                        let mut autoderef = self.autoderef(span, actual);
+                        let mut autoderef = self.autoderef(span, rcvr_ty);
                         let candidate_found = autoderef.any(|(ty, _)| {
                             if let ty::Adt(adt_def, _) = ty.kind() {
                                 self.tcx
@@ -321,16 +320,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     "no {} named `{}` found for {} `{}` in the current scope",
                     item_kind,
                     item_name,
-                    actual.prefix_string(self.tcx),
+                    rcvr_ty.prefix_string(self.tcx),
                     ty_str_reported,
                 );
-                if actual.references_error() {
+                if rcvr_ty.references_error() {
                     err.downgrade_to_delayed_bug();
                 }
 
                 if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
                     self.suggest_await_before_method(
-                        &mut err, item_name, actual, cal, span,
+                        &mut err, item_name, rcvr_ty, cal, span,
                     );
                 }
                 if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
@@ -341,7 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         Applicability::MachineApplicable,
                     );
                 }
-                if let ty::RawPtr(_) = &actual.kind() {
+                if let ty::RawPtr(_) = &rcvr_ty.kind() {
                     err.note(
                         "try using `<*const T>::as_ref()` to get a reference to the \
                          type behind the pointer: https://doc.rust-lang.org/std/\
@@ -353,7 +352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
 
-                let ty_span = match actual.kind() {
+                let ty_span = match rcvr_ty.kind() {
                     ty::Param(param_type) => Some(
                         param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()),
                     ),
@@ -365,7 +364,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         span,
                         format!(
                             "{item_kind} `{item_name}` not found for this {}",
-                            actual.prefix_string(self.tcx)
+                            rcvr_ty.prefix_string(self.tcx)
                         ),
                     );
                 }
@@ -377,7 +376,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .hir()
                             .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
                         let probe = self.lookup_probe(
-                            span,
                             item_name,
                             output_ty,
                             call_expr,
@@ -398,122 +396,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     custom_span_label = true;
                 }
                 if static_candidates.len() == 1 {
-                    let mut has_unsuggestable_args = false;
-                    let ty_str = if let Some(CandidateSource::Impl(impl_did)) =
-                        static_candidates.get(0)
-                    {
-                        // When the "method" is resolved through dereferencing, we really want the
-                        // original type that has the associated function for accurate suggestions.
-                        // (#61411)
-                        let ty = tcx.at(span).type_of(*impl_did);
-                        match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
-                            (ty::Adt(def, _), ty::Adt(def_actual, substs)) if def == def_actual => {
-                                // If there are any inferred arguments, (`{integer}`), we should replace
-                                // them with underscores to allow the compiler to infer them
-                                let infer_substs: Vec<GenericArg<'_>> = substs
-                                    .into_iter()
-                                    .map(|arg| {
-                                        if !arg.is_suggestable(tcx, true) {
-                                            has_unsuggestable_args = true;
-                                            match arg.unpack() {
-                                            GenericArgKind::Lifetime(_) => self
-                                                .next_region_var(RegionVariableOrigin::MiscVariable(
-                                                    rustc_span::DUMMY_SP,
-                                                ))
-                                                .into(),
-                                            GenericArgKind::Type(_) => self
-                                                .next_ty_var(TypeVariableOrigin {
-                                                    span: rustc_span::DUMMY_SP,
-                                                    kind: TypeVariableOriginKind::MiscVariable,
-                                                })
-                                                .into(),
-                                            GenericArgKind::Const(arg) => self
-                                                .next_const_var(
-                                                    arg.ty(),
-                                                    ConstVariableOrigin {
-                                                        span: rustc_span::DUMMY_SP,
-                                                        kind: ConstVariableOriginKind::MiscVariable,
-                                                    },
-                                                )
-                                                .into(),
-                                            }
-                                        } else {
-                                            arg
-                                        }
-                                    })
-                                    .collect::<Vec<_>>();
-
-                                tcx.value_path_str_with_substs(
-                                    def_actual.did(),
-                                    tcx.intern_substs(&infer_substs),
-                                )
-                            }
-                            _ => self.ty_to_value_string(ty.peel_refs()),
-                        }
-                    } else {
-                        self.ty_to_value_string(actual.peel_refs())
-                    };
-                    if let SelfSource::MethodCall(_) = source {
-                        let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) &&
-                            let Some(assoc) = self.associated_value(*impl_did, item_name) {
-                            let sig = self.tcx.fn_sig(assoc.def_id);
-                            if let Some(first) = sig.inputs().skip_binder().get(0) {
-                                if first.peel_refs() == rcvr_ty.peel_refs() {
-                                    None
-                                } else {
-                                    Some(if first.is_region_ptr() {
-                                        if first.is_mutable_ptr() { "&mut " } else { "&" }
-                                    } else {
-                                        ""
-                                    })
-                                }
-                            } else {
-                                None
-                            }
-                        } else {
-                            None
-                        };
-                        let mut applicability = Applicability::MachineApplicable;
-                        let args = if let Some((receiver, args)) = args {
-                            // The first arg is the same kind as the receiver
-                            let explicit_args = if first_arg.is_some() {
-                                std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
-                            } else {
-                                // There is no `Self` kind to infer the arguments from
-                                if has_unsuggestable_args {
-                                    applicability = Applicability::HasPlaceholders;
-                                }
-                                args.iter().collect()
-                            };
-                            format!(
-                                "({}{})",
-                                first_arg.unwrap_or(""),
-                                explicit_args
-                                    .iter()
-                                    .map(|arg| tcx
-                                        .sess
-                                        .source_map()
-                                        .span_to_snippet(arg.span)
-                                        .unwrap_or_else(|_| {
-                                            applicability = Applicability::HasPlaceholders;
-                                            "_".to_owned()
-                                        }))
-                                    .collect::<Vec<_>>()
-                                    .join(", "),
-                            )
-                        } else {
-                            applicability = Applicability::HasPlaceholders;
-                            "(...)".to_owned()
-                        };
-                        err.span_suggestion(
-                            sugg_span,
-                            "use associated function syntax instead",
-                            format!("{}::{}{}", ty_str, item_name, args),
-                            applicability,
-                        );
-                    } else {
-                        err.help(&format!("try with `{}::{}`", ty_str, item_name,));
-                    }
+                    self.suggest_associated_call_syntax(
+                        &mut err,
+                        &static_candidates,
+                        rcvr_ty,
+                        source,
+                        item_name,
+                        args,
+                        sugg_span,
+                    );
 
                     report_candidates(span, &mut err, &mut static_candidates, sugg_span);
                 } else if static_candidates.len() > 1 {
@@ -523,7 +414,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let mut bound_spans = vec![];
                 let mut restrict_type_params = false;
                 let mut unsatisfied_bounds = false;
-                if item_name.name == sym::count && self.is_slice_ty(actual, span) {
+                if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
                     let msg = "consider using `len` instead";
                     if let SelfSource::MethodCall(_expr) = source {
                         err.span_suggestion_short(
@@ -537,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
                         let iterator_trait = self.tcx.def_path_str(iterator_trait);
-                        err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement"));
+                        err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"));
                     }
                 } else if !unsatisfied_predicates.is_empty() {
                     let mut type_params = FxHashMap::default();
@@ -876,7 +767,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             .map(|(_, path)| path)
                             .collect::<Vec<_>>()
                             .join("\n");
-                        let actual_prefix = actual.prefix_string(self.tcx);
+                        let actual_prefix = rcvr_ty.prefix_string(self.tcx);
                         info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
                         let (primary_message, label) =
                             if unimplemented_traits.len() == 1 && unimplemented_traits_only {
@@ -885,7 +776,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     .next()
                                     .map(|(_, (trait_ref, obligation))| {
                                         if trait_ref.self_ty().references_error()
-                                            || actual.references_error()
+                                            || rcvr_ty.references_error()
                                         {
                                             // Avoid crashing.
                                             return (None, None);
@@ -921,7 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let label_span_not_found = |err: &mut Diagnostic| {
                     if unsatisfied_predicates.is_empty() {
                         err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
-                        let is_string_or_ref_str = match actual.kind() {
+                        let is_string_or_ref_str = match rcvr_ty.kind() {
                             ty::Ref(_, ty, _) => {
                                 ty.is_str()
                                     || matches!(
@@ -957,7 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                                 // different from the received one
                                                 // So we avoid suggestion method with Box<Self>
                                                 // for instance
-                                                self.tcx.at(span).type_of(*def_id) != actual
+                                                self.tcx.at(span).type_of(*def_id) != rcvr_ty
                                                     && self.tcx.at(span).type_of(*def_id) != rcvr_ty
                                             }
                                             (Mode::Path, false, _) => true,
@@ -1017,10 +908,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
                 // can't be called due to `typeof(expr): Clone` not holding.
                 if unsatisfied_predicates.is_empty() {
-                    self.suggest_calling_method_on_field(&mut err, source, span, actual, item_name);
+                    self.suggest_calling_method_on_field(
+                        &mut err, source, span, rcvr_ty, item_name,
+                    );
                 }
 
-                self.check_for_inner_self(&mut err, source, span, actual, item_name);
+                self.check_for_inner_self(&mut err, source, rcvr_ty, item_name);
 
                 bound_spans.sort();
                 bound_spans.dedup();
@@ -1028,7 +921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     err.span_label(span, &msg);
                 }
 
-                if actual.is_numeric() && actual.is_fresh() || restrict_type_params {
+                if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
                 } else {
                     self.suggest_traits_to_import(
                         &mut err,
@@ -1046,8 +939,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // Don't emit a suggestion if we found an actual method
                 // that had unsatisfied trait bounds
-                if unsatisfied_predicates.is_empty() && actual.is_enum() {
-                    let adt_def = actual.ty_adt_def().expect("enum is not an ADT");
+                if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
+                    let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT");
                     if let Some(suggestion) = lev_distance::find_best_match_for_name(
                         &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
                         item_name.name,
@@ -1062,7 +955,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
 
-                if item_name.name == sym::as_str && actual.peel_refs().is_str() {
+                if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() {
                     let msg = "remove this method call";
                     let mut fallback_span = true;
                     if let SelfSource::MethodCall(expr) = source {
@@ -1178,6 +1071,138 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         None
     }
 
+    /// Suggest calling `Ty::method` if `.method()` isn't found because the method
+    /// doesn't take a `self` receiver.
+    fn suggest_associated_call_syntax(
+        &self,
+        err: &mut Diagnostic,
+        static_candidates: &Vec<CandidateSource>,
+        rcvr_ty: Ty<'tcx>,
+        source: SelfSource<'tcx>,
+        item_name: Ident,
+        args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>,
+        sugg_span: Span,
+    ) {
+        let mut has_unsuggestable_args = false;
+        let ty_str = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0) {
+            // When the "method" is resolved through dereferencing, we really want the
+            // original type that has the associated function for accurate suggestions.
+            // (#61411)
+            let impl_ty = self.tcx.type_of(*impl_did);
+            let target_ty = self
+                .autoderef(sugg_span, rcvr_ty)
+                .find(|(rcvr_ty, _)| {
+                    DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }
+                        .types_may_unify(*rcvr_ty, impl_ty)
+                })
+                .map_or(impl_ty, |(ty, _)| ty)
+                .peel_refs();
+            if let ty::Adt(def, substs) = target_ty.kind() {
+                // If there are any inferred arguments, (`{integer}`), we should replace
+                // them with underscores to allow the compiler to infer them
+                let infer_substs = self.tcx.mk_substs(substs.into_iter().map(|arg| {
+                    if !arg.is_suggestable(self.tcx, true) {
+                        has_unsuggestable_args = true;
+                        match arg.unpack() {
+                            GenericArgKind::Lifetime(_) => self
+                                .next_region_var(RegionVariableOrigin::MiscVariable(
+                                    rustc_span::DUMMY_SP,
+                                ))
+                                .into(),
+                            GenericArgKind::Type(_) => self
+                                .next_ty_var(TypeVariableOrigin {
+                                    span: rustc_span::DUMMY_SP,
+                                    kind: TypeVariableOriginKind::MiscVariable,
+                                })
+                                .into(),
+                            GenericArgKind::Const(arg) => self
+                                .next_const_var(
+                                    arg.ty(),
+                                    ConstVariableOrigin {
+                                        span: rustc_span::DUMMY_SP,
+                                        kind: ConstVariableOriginKind::MiscVariable,
+                                    },
+                                )
+                                .into(),
+                        }
+                    } else {
+                        arg
+                    }
+                }));
+
+                self.tcx.value_path_str_with_substs(def.did(), infer_substs)
+            } else {
+                self.ty_to_value_string(target_ty)
+            }
+        } else {
+            self.ty_to_value_string(rcvr_ty.peel_refs())
+        };
+        if let SelfSource::MethodCall(_) = source {
+            let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0)
+                && let Some(assoc) = self.associated_value(*impl_did, item_name)
+                && assoc.kind == ty::AssocKind::Fn
+            {
+                let sig = self.tcx.fn_sig(assoc.def_id);
+                if let Some(first) = sig.inputs().skip_binder().get(0) {
+                    if first.peel_refs() == rcvr_ty.peel_refs() {
+                        None
+                    } else {
+                        Some(if first.is_region_ptr() {
+                            if first.is_mutable_ptr() { "&mut " } else { "&" }
+                        } else {
+                            ""
+                        })
+                    }
+                } else {
+                    None
+                }
+            } else {
+                None
+            };
+            let mut applicability = Applicability::MachineApplicable;
+            let args = if let Some((receiver, args)) = args {
+                // The first arg is the same kind as the receiver
+                let explicit_args = if first_arg.is_some() {
+                    std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
+                } else {
+                    // There is no `Self` kind to infer the arguments from
+                    if has_unsuggestable_args {
+                        applicability = Applicability::HasPlaceholders;
+                    }
+                    args.iter().collect()
+                };
+                format!(
+                    "({}{})",
+                    first_arg.unwrap_or(""),
+                    explicit_args
+                        .iter()
+                        .map(|arg| self
+                            .tcx
+                            .sess
+                            .source_map()
+                            .span_to_snippet(arg.span)
+                            .unwrap_or_else(|_| {
+                                applicability = Applicability::HasPlaceholders;
+                                "_".to_owned()
+                            }))
+                        .collect::<Vec<_>>()
+                        .join(", "),
+                )
+            } else {
+                applicability = Applicability::HasPlaceholders;
+                "(...)".to_owned()
+            };
+            err.span_suggestion(
+                sugg_span,
+                "use associated function syntax instead",
+                format!("{}::{}{}", ty_str, item_name, args),
+                applicability,
+            );
+        } else {
+            err.help(&format!("try with `{}::{}`", ty_str, item_name,));
+        }
+    }
+
     /// Suggest calling a field with a type that implements the `Fn*` traits instead of a method with
     /// the same name as the field i.e. `(a.my_fn_ptr)(10)` instead of `a.my_fn_ptr(10)`.
     fn suggest_calling_field_as_fn(
@@ -1295,7 +1320,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
 
                     let pick = self.probe_for_name(
-                        span,
                         Mode::MethodCall,
                         item_name,
                         IsSuggestion(true),
@@ -1474,7 +1498,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         span,
                         &|_, field_ty| {
                             self.lookup_probe(
-                                span,
                                 item_name,
                                 field_ty,
                                 call_expr,
@@ -1522,7 +1545,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         err: &mut Diagnostic,
         source: SelfSource<'tcx>,
-        span: Span,
         actual: Ty<'tcx>,
         item_name: Ident,
     ) {
@@ -1545,15 +1567,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             return None;
                         }
 
-                        self.lookup_probe(
-                            span,
-                            item_name,
-                            field_ty,
-                            call_expr,
-                            ProbeScope::TraitsInScope,
-                        )
-                        .ok()
-                        .map(|pick| (variant, field, pick))
+                        self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope)
+                            .ok()
+                            .map(|pick| (variant, field, pick))
                     })
                     .collect();
 
@@ -1618,12 +1634,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let [first] = ***substs else { return; };
                 let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
                 let Ok(pick) = self.lookup_probe(
-                            span,
-                            item_name,
-                            ty,
-                            call_expr,
-                            ProbeScope::TraitsInScope,
-                        )  else { return; };
+                    item_name,
+                    ty,
+                    call_expr,
+                    ProbeScope::TraitsInScope,
+                )  else { return; };
 
                 let name = self.ty_to_value_string(actual);
                 let inner_id = kind.did();
@@ -1873,7 +1888,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let SelfSource::QPath(ty) = self_source else { return; };
         for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) {
             if let Ok(pick) = self.probe_for_name(
-                ty.span,
                 Mode::Path,
                 item_name,
                 IsSuggestion(true),
@@ -2081,7 +2095,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
                 (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
             ] {
-                match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) {
+                match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) {
                     Ok(pick) => {
                         // If the method is defined for the receiver we have, it likely wasn't `use`d.
                         // We point at the method, but we just skip the rest of the check for arbitrary
@@ -2115,7 +2129,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ] {
                     if let Some(new_rcvr_t) = *rcvr_ty
                         && let Ok(pick) = self.lookup_probe(
-                            span,
                             item_name,
                             new_rcvr_t,
                             rcvr,
@@ -2496,7 +2509,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 span: method_name.span,
             };
             let probe = self.lookup_probe(
-                expr.span,
                 new_name,
                 self_ty,
                 self_expr,
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 1e26daa9c2c..2eca40d678a 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -514,7 +514,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         for (&def_id, c_sig) in fcx_typeck_results.user_provided_sigs.iter() {
             if cfg!(debug_assertions) && c_sig.needs_infer() {
                 span_bug!(
-                    self.fcx.tcx.hir().span_if_local(def_id).unwrap(),
+                    self.fcx.tcx.def_span(def_id),
                     "writeback: `{:?}` has inference variables",
                     c_sig
                 );
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index d4140cb295f..3fbabbc6344 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -167,11 +167,15 @@ pub enum DocStyle {
     Inner,
 }
 
+// Note that the suffix is *not* considered when deciding the `LiteralKind` in
+// this type. This means that float literals like `1f32` are classified by this
+// type as `Int`. (Compare against `rustc_ast::token::LitKind` and
+// `rustc_ast::ast::LitKind.)
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub enum LiteralKind {
-    /// "12_u8", "0o100", "0b120i99"
+    /// "12_u8", "0o100", "0b120i99", "1f32".
     Int { base: Base, empty_int: bool },
-    /// "12.34f32", "0b100.100"
+    /// "12.34f32", "1e3", but not "1f32`.
     Float { base: Base, empty_exponent: bool },
     /// "'a'", "'\\'", "'''", "';"
     Char { terminated: bool },
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 27c04d82811..c2d0a662ddb 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -98,9 +98,10 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
 impl EarlyLintPass for WhileTrue {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         if let ast::ExprKind::While(cond, _, label) = &e.kind
-            && let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind
-            && let ast::LitKind::Bool(true) = lit.kind
-            && !lit.span.from_expansion()
+            && let cond = pierce_parens(cond)
+            && let ast::ExprKind::Lit(token_lit) = cond.kind
+            && let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit
+            && !cond.span.from_expansion()
         {
             let condition_span = e.span.with_hi(cond.span.hi());
             cx.struct_span_lint(
@@ -185,9 +186,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers {
         // If it's a struct, we also have to check the fields' types
         match it.kind {
             hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
-                for struct_field in struct_def.fields() {
-                    let def_id = cx.tcx.hir().local_def_id(struct_field.hir_id);
-                    self.check_heap_type(cx, struct_field.span, cx.tcx.type_of(def_id));
+                for field in struct_def.fields() {
+                    self.check_heap_type(cx, field.span, cx.tcx.type_of(field.def_id));
                 }
             }
             _ => (),
@@ -673,13 +673,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
 
     fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) {
         if !sf.is_positional() {
-            let def_id = cx.tcx.hir().local_def_id(sf.hir_id);
-            self.check_missing_docs_attrs(cx, def_id, "a", "struct field")
+            self.check_missing_docs_attrs(cx, sf.def_id, "a", "struct field")
         }
     }
 
     fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) {
-        self.check_missing_docs_attrs(cx, cx.tcx.hir().local_def_id(v.id), "a", "variant");
+        self.check_missing_docs_attrs(cx, v.def_id, "a", "variant");
     }
 }
 
@@ -1424,11 +1423,10 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
 
     fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
         let map = cx.tcx.hir();
-        let def_id = map.local_def_id(field.hir_id);
         if matches!(map.get(map.get_parent_node(field.hir_id)), Node::Variant(_)) {
             return;
         }
-        self.perform_lint(cx, "field", def_id, field.vis_span, false);
+        self.perform_lint(cx, "field", field.def_id, field.vis_span, false);
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
index 7e884e990ce..7106e75dba2 100644
--- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
+++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
@@ -123,23 +123,22 @@ impl EarlyLintPass for HiddenUnicodeCodepoints {
 
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
         // byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString`
-        let (text, span, padding) = match &expr.kind {
-            ast::ExprKind::Lit(ast::Lit { token_lit, kind, span }) => {
+        match &expr.kind {
+            ast::ExprKind::Lit(token_lit) => {
                 let text = token_lit.symbol;
                 if !contains_text_flow_control_chars(text.as_str()) {
                     return;
                 }
-                let padding = match kind {
+                let padding = match token_lit.kind {
                     // account for `"` or `'`
-                    ast::LitKind::Str(_, ast::StrStyle::Cooked) | ast::LitKind::Char(_) => 1,
+                    ast::token::LitKind::Str | ast::token::LitKind::Char => 1,
                     // account for `r###"`
-                    ast::LitKind::Str(_, ast::StrStyle::Raw(val)) => *val as u32 + 2,
+                    ast::token::LitKind::StrRaw(n) => n as u32 + 2,
                     _ => return,
                 };
-                (text, span, padding)
+                self.lint_text_direction_codepoint(cx, text, expr.span, padding, true, "literal");
             }
-            _ => return,
+            _ => {}
         };
-        self.lint_text_direction_codepoint(cx, text, *span, padding, true, "literal");
     }
 }
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 303fcb1a1d1..f484e31ba15 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -205,7 +205,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
     }
 
     fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
-        self.with_lint_attrs(v.id, |cx| {
+        self.with_lint_attrs(v.hir_id, |cx| {
             lint_callback!(cx, check_variant, v);
             hir_visit::walk_variant(cx, v);
         })
diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs
index 78f355ec3d0..04d844d21dc 100644
--- a/compiler/rustc_lint/src/let_underscore.rs
+++ b/compiler/rustc_lint/src/let_underscore.rs
@@ -11,7 +11,8 @@ declare_lint! {
     /// scope.
     ///
     /// ### Example
-    /// ```
+    ///
+    /// ```rust
     /// struct SomeStruct;
     /// impl Drop for SomeStruct {
     ///     fn drop(&mut self) {
@@ -56,7 +57,7 @@ declare_lint! {
     /// of at end of scope, which is typically incorrect.
     ///
     /// ### Example
-    /// ```compile_fail
+    /// ```rust,compile_fail
     /// use std::sync::{Arc, Mutex};
     /// use std::thread;
     /// let data = Arc::new(Mutex::new(0));
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index efae2669006..847c356b83c 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -320,7 +320,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
     }
 
     fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
-        self.add_id(v.id);
+        self.add_id(v.hir_id);
         intravisit::walk_variant(self, v);
     }
 
@@ -392,7 +392,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'
     }
 
     fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
-        self.add_id(v.id);
+        self.add_id(v.hir_id);
         intravisit::walk_variant(self, v);
     }
 
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 7443d131c64..619582c0539 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -26,19 +26,23 @@ declare_lint! {
     ///
     /// ### Example
     ///
-    /// ```
+    /// ```rust
+    /// trait Duh {}
+    ///
+    /// impl Duh for i32 {}
+    ///
     /// trait Trait {
-    ///     type Assoc: Send;
+    ///     type Assoc: Duh;
     /// }
     ///
     /// struct Struct;
     ///
-    /// impl Trait for Struct {
-    ///     type Assoc = i32;
+    /// impl<F: Duh> Trait for F {
+    ///     type Assoc = F;
     /// }
     ///
     /// fn test() -> impl Trait<Assoc = impl Sized> {
-    ///     Struct
+    ///     42
     /// }
     /// ```
     ///
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 37caab2da0f..afc568f3a50 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -360,7 +360,7 @@ fn lint_int_literal<'tcx>(
         }
 
         if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) {
-            // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`.
+            // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
             return;
         }
 
@@ -429,7 +429,7 @@ fn lint_uint_literal<'tcx>(
             }
         }
         if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) {
-            // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`.
+            // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`.
             return;
         }
         if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
@@ -1195,35 +1195,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     }
 
     fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
-        struct ProhibitOpaqueTypes<'a, 'tcx> {
-            cx: &'a LateContext<'tcx>,
-        }
-
-        impl<'a, 'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> {
+        struct ProhibitOpaqueTypes;
+        impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueTypes {
             type BreakTy = Ty<'tcx>;
 
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-                match ty.kind() {
-                    ty::Opaque(..) => ControlFlow::Break(ty),
-                    // Consider opaque types within projections FFI-safe if they do not normalize
-                    // to more opaque types.
-                    ty::Projection(..) => {
-                        let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty);
-
-                        // If `ty` is an opaque type directly then `super_visit_with` won't invoke
-                        // this function again.
-                        if ty.has_opaque_types() {
-                            self.visit_ty(ty)
-                        } else {
-                            ControlFlow::CONTINUE
-                        }
-                    }
-                    _ => ty.super_visit_with(self),
+                if !ty.has_opaque_types() {
+                    return ControlFlow::CONTINUE;
+                }
+
+                if let ty::Opaque(..) = ty.kind() {
+                    ControlFlow::Break(ty)
+                } else {
+                    ty.super_visit_with(self)
                 }
             }
         }
 
-        if let Some(ty) = ty.visit_with(&mut ProhibitOpaqueTypes { cx: self.cx }).break_value() {
+        if let Some(ty) = self
+            .cx
+            .tcx
+            .normalize_erasing_regions(self.cx.param_env, ty)
+            .visit_with(&mut ProhibitOpaqueTypes)
+            .break_value()
+        {
             self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
             true
         } else {
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 389f3ccf72a..b80facb1759 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -605,7 +605,7 @@ declare_lint! {
     ///
     /// ### Example
     ///
-    /// ```
+    /// ```rust
     /// #[warn(unused_tuple_struct_fields)]
     /// struct S(i32, i32, i32);
     /// let s = S(1, 2, 3);
@@ -1154,7 +1154,7 @@ declare_lint! {
     ///
     /// ### Example
     ///
-    /// ```compile_fail
+    /// ```rust,compile_fail
     /// #[repr(packed)]
     /// pub struct Foo {
     ///     field1: u64,
@@ -2548,7 +2548,7 @@ declare_lint! {
     ///
     /// ### Example
     ///
-    /// ```compile_fail
+    /// ```rust,compile_fail
     /// # #![allow(unused)]
     /// enum E {
     ///     A,
@@ -3918,7 +3918,7 @@ declare_lint! {
     ///
     /// ### Example
     ///
-    /// ```
+    /// ```rust
     /// #![allow(test_unstable_lint)]
     /// ```
     ///
diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs
index 28e092c1eb7..d35e4191cc0 100644
--- a/compiler/rustc_llvm/build.rs
+++ b/compiler/rustc_llvm/build.rs
@@ -334,7 +334,7 @@ fn main() {
         "c++"
     } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
         // NetBSD uses a separate library when relocation is required
-        "stdc++_pic"
+        "stdc++_p"
     } else if llvm_use_libcxx.is_some() {
         "c++"
     } else {
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 0d9b5a57b69..6f8bb676104 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -9,7 +9,7 @@
 #include "llvm/IR/IntrinsicsARM.h"
 #include "llvm/IR/Mangler.h"
 #if LLVM_VERSION_GE(16, 0)
-#include "llvm/IR/ModRef.h"
+#include "llvm/Support/ModRef.h"
 #endif
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/COFFImportFile.h"
@@ -1111,6 +1111,10 @@ extern "C" uint64_t LLVMRustDIBuilderCreateOpPlusUconst() {
   return dwarf::DW_OP_plus_uconst;
 }
 
+extern "C" int64_t LLVMRustDIBuilderCreateOpLLVMFragment() {
+  return dwarf::DW_OP_LLVM_fragment;
+}
+
 extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) {
   RawRustStringOstream OS(Str);
   unwrap<llvm::Type>(Ty)->print(OS);
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index f360a586476..c41ae8d55cd 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -22,9 +22,14 @@ pub const METADATA_FILENAME: &str = "lib.rmeta";
 /// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a
 /// directory being searched for `extern crate` (observing an incomplete file).
 /// The returned path is the temporary file containing the complete metadata.
-pub fn emit_metadata(sess: &Session, metadata: &[u8], tmpdir: &MaybeTempDir) -> PathBuf {
-    let out_filename = tmpdir.as_ref().join(METADATA_FILENAME);
-    let result = fs::write(&out_filename, metadata);
+pub fn emit_wrapper_file(
+    sess: &Session,
+    data: &[u8],
+    tmpdir: &MaybeTempDir,
+    name: &str,
+) -> PathBuf {
+    let out_filename = tmpdir.as_ref().join(name);
+    let result = fs::write(&out_filename, data);
 
     if let Err(err) = result {
         sess.emit_fatal(FailedWriteError { filename: out_filename, err });
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 98cf6fef54a..1987f88e6b8 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -41,6 +41,6 @@ pub mod errors;
 pub mod fs;
 pub mod locator;
 
-pub use fs::{emit_metadata, METADATA_FILENAME};
+pub use fs::{emit_wrapper_file, METADATA_FILENAME};
 pub use native_libs::find_native_static_library;
 pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 6a73e14e9f5..e09ac968b60 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1558,9 +1558,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 // Encode def_ids for each field and method
                 // for methods, write all the stuff get_trait_method
                 // needs to know
-                let ctor = struct_def
-                    .ctor_hir_id()
-                    .map(|ctor_hir_id| self.tcx.hir().local_def_id(ctor_hir_id).local_def_index);
+                let ctor = struct_def.ctor_def_id().map(|ctor_def_id| ctor_def_id.local_def_index);
 
                 let variant = adt_def.non_enum_variant();
                 record!(self.tables.variant_data[def_id] <- VariantData {
@@ -1685,8 +1683,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             hir::ItemKind::Struct(ref struct_def, _) => {
                 let def = self.tcx.adt_def(item.owner_id.to_def_id());
                 // If the struct has a constructor, encode it.
-                if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
-                    let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
+                if let Some(ctor_def_id) = struct_def.ctor_def_id() {
                     self.encode_struct_ctor(def, ctor_def_id.to_def_id());
                 }
             }
@@ -1708,12 +1705,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         }
     }
 
-    fn encode_info_for_closure(&mut self, hir_id: hir::HirId) {
-        let def_id = self.tcx.hir().local_def_id(hir_id);
-        debug!("EncodeContext::encode_info_for_closure({:?})", def_id);
+    #[instrument(level = "debug", skip(self))]
+    fn encode_info_for_closure(&mut self, def_id: LocalDefId) {
         // NOTE(eddyb) `tcx.type_of(def_id)` isn't used because it's fully generic,
         // including on the signature, which is inferred in `typeck.
         let typeck_result: &'tcx ty::TypeckResults<'tcx> = self.tcx.typeck(def_id);
+        let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
         let ty = typeck_result.node_type(hir_id);
         match ty.kind() {
             ty::Generator(..) => {
@@ -2101,11 +2098,10 @@ impl<'a, 'tcx> Visitor<'tcx> for EncodeContext<'a, 'tcx> {
 impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_info_for_generics(&mut self, generics: &hir::Generics<'tcx>) {
         for param in generics.params {
-            let def_id = self.tcx.hir().local_def_id(param.hir_id);
             match param.kind {
                 hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => {}
                 hir::GenericParamKind::Const { ref default, .. } => {
-                    let def_id = def_id.to_def_id();
+                    let def_id = param.def_id.to_def_id();
                     if default.is_some() {
                         record!(self.tables.const_param_default[def_id] <- self.tcx.const_param_default(def_id))
                     }
@@ -2115,8 +2111,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     }
 
     fn encode_info_for_expr(&mut self, expr: &hir::Expr<'_>) {
-        if let hir::ExprKind::Closure { .. } = expr.kind {
-            self.encode_info_for_closure(expr.hir_id);
+        if let hir::ExprKind::Closure(closure) = expr.kind {
+            self.encode_info_for_closure(closure.def_id);
         }
     }
 }
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 8e7d0cf2ab1..5f6e498dbea 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -8,7 +8,7 @@ doctest = false
 
 [dependencies]
 bitflags = "1.2.1"
-chalk-ir = "0.80.0"
+chalk-ir = "0.87.0"
 either = "1.5.0"
 gsgdt = "0.1.2"
 polonius-engine = "0.13.0"
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 14f50ae87de..e14ea7be9cf 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -1086,10 +1086,10 @@ impl<'hir> Map<'hir> {
 
     /// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when
     /// called with the HirId for the `{ ... }` anon const
-    pub fn opt_const_param_default_param_hir_id(self, anon_const: HirId) -> Option<HirId> {
+    pub fn opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option<LocalDefId> {
         match self.get(self.get_parent_node(anon_const)) {
             Node::GenericParam(GenericParam {
-                hir_id: param_id,
+                def_id: param_id,
                 kind: GenericParamKind::Const { .. },
                 ..
             }) => Some(*param_id),
@@ -1198,20 +1198,7 @@ fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> {
 fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
     let id_str = format!(" (hir_id={})", id);
 
-    let path_str = || {
-        // This functionality is used for debugging, try to use `TyCtxt` to get
-        // the user-friendly path, otherwise fall back to stringifying `DefPath`.
-        crate::ty::tls::with_opt(|tcx| {
-            if let Some(tcx) = tcx {
-                let def_id = map.local_def_id(id);
-                tcx.def_path_str(def_id.to_def_id())
-            } else if let Some(path) = map.def_path_from_hir_id(id) {
-                path.data.into_iter().map(|elem| elem.to_string()).collect::<Vec<_>>().join("::")
-            } else {
-                String::from("<missing path>")
-            }
-        })
-    };
+    let path_str = |def_id: LocalDefId| map.tcx.def_path_str(def_id.to_def_id());
 
     let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default();
     let node_str = |prefix| format!("{} {}{}", prefix, span_str(), id_str);
@@ -1243,18 +1230,19 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
                 ItemKind::TraitAlias(..) => "trait alias",
                 ItemKind::Impl { .. } => "impl",
             };
-            format!("{} {}{}", item_str, path_str(), id_str)
+            format!("{} {}{}", item_str, path_str(item.owner_id.def_id), id_str)
+        }
+        Some(Node::ForeignItem(item)) => {
+            format!("foreign item {}{}", path_str(item.owner_id.def_id), id_str)
+        }
+        Some(Node::ImplItem(ii)) => {
+            let kind = match ii.kind {
+                ImplItemKind::Const(..) => "assoc const",
+                ImplItemKind::Fn(..) => "method",
+                ImplItemKind::Type(_) => "assoc type",
+            };
+            format!("{} {} in {}{}", kind, ii.ident, path_str(ii.owner_id.def_id), id_str)
         }
-        Some(Node::ForeignItem(_)) => format!("foreign item {}{}", path_str(), id_str),
-        Some(Node::ImplItem(ii)) => match ii.kind {
-            ImplItemKind::Const(..) => {
-                format!("assoc const {} in {}{}", ii.ident, path_str(), id_str)
-            }
-            ImplItemKind::Fn(..) => format!("method {} in {}{}", ii.ident, path_str(), id_str),
-            ImplItemKind::Type(_) => {
-                format!("assoc type {} in {}{}", ii.ident, path_str(), id_str)
-            }
-        },
         Some(Node::TraitItem(ti)) => {
             let kind = match ti.kind {
                 TraitItemKind::Const(..) => "assoc constant",
@@ -1262,13 +1250,13 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
                 TraitItemKind::Type(..) => "assoc type",
             };
 
-            format!("{} {} in {}{}", kind, ti.ident, path_str(), id_str)
+            format!("{} {} in {}{}", kind, ti.ident, path_str(ti.owner_id.def_id), id_str)
         }
         Some(Node::Variant(ref variant)) => {
-            format!("variant {} in {}{}", variant.ident, path_str(), id_str)
+            format!("variant {} in {}{}", variant.ident, path_str(variant.def_id), id_str)
         }
         Some(Node::Field(ref field)) => {
-            format!("field {} in {}{}", field.ident, path_str(), id_str)
+            format!("field {} in {}{}", field.ident, path_str(field.def_id), id_str)
         }
         Some(Node::AnonConst(_)) => node_str("const"),
         Some(Node::Expr(_)) => node_str("expr"),
@@ -1285,9 +1273,15 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
         Some(Node::Block(_)) => node_str("block"),
         Some(Node::Infer(_)) => node_str("infer"),
         Some(Node::Local(_)) => node_str("local"),
-        Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str),
+        Some(Node::Ctor(ctor)) => format!(
+            "ctor {}{}",
+            ctor.ctor_def_id().map_or("<missing path>".into(), |def_id| path_str(def_id)),
+            id_str
+        ),
         Some(Node::Lifetime(_)) => node_str("lifetime"),
-        Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str),
+        Some(Node::GenericParam(ref param)) => {
+            format!("generic_param {}{}", path_str(param.def_id), id_str)
+        }
         Some(Node::Crate(..)) => String::from("root_crate"),
         None => format!("unknown node{}", id_str),
     }
@@ -1407,13 +1401,13 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
     }
 
     fn visit_anon_const(&mut self, c: &'hir AnonConst) {
-        self.body_owners.push(self.tcx.hir().local_def_id(c.hir_id));
+        self.body_owners.push(c.def_id);
         intravisit::walk_anon_const(self, c)
     }
 
     fn visit_expr(&mut self, ex: &'hir Expr<'hir>) {
-        if matches!(ex.kind, ExprKind::Closure { .. }) {
-            self.body_owners.push(self.tcx.hir().local_def_id(ex.hir_id));
+        if let ExprKind::Closure(closure) = ex.kind {
+            self.body_owners.push(closure.def_id);
         }
         intravisit::walk_expr(self, ex)
     }
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index a58cbc3767e..6bdf591fdd7 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -43,7 +43,6 @@
 #![feature(type_alias_impl_trait)]
 #![feature(associated_type_bounds)]
 #![feature(rustc_attrs)]
-#![cfg_attr(bootstrap, feature(half_open_range_patterns))]
 #![feature(control_flow_enum)]
 #![feature(associated_type_defaults)]
 #![feature(trusted_step)]
diff --git a/compiler/rustc_middle/src/mir/generic_graphviz.rs b/compiler/rustc_middle/src/mir/generic_graphviz.rs
index 11ac45943ac..ccae7e159b1 100644
--- a/compiler/rustc_middle/src/mir/generic_graphviz.rs
+++ b/compiler/rustc_middle/src/mir/generic_graphviz.rs
@@ -126,7 +126,7 @@ impl<
             write!(
                 w,
                 r#"<tr><td align="left" balign="left">{}</td></tr>"#,
-                dot::escape_html(&section).replace('\n', "<br/>")
+                dot::escape_html(&section)
             )?;
         }
 
@@ -147,7 +147,7 @@ impl<
             let src = self.node(source);
             let trg = self.node(target);
             let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) {
-                dot::escape_html(edge_label).replace('\n', r#"<br align="left"/>"#)
+                dot::escape_html(edge_label)
             } else {
                 "".to_owned()
             };
@@ -162,8 +162,7 @@ impl<
     where
         W: Write,
     {
-        let lines = label.split('\n').map(|s| dot::escape_html(s)).collect::<Vec<_>>();
-        let escaped_label = lines.join(r#"<br align="left"/>"#);
+        let escaped_label = dot::escape_html(label);
         writeln!(w, r#"    label=<<br/><br/>{}<br align="left"/><br/><br/><br/>>;"#, escaped_label)
     }
 
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 37ec04b07f8..68024070346 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -1,16 +1,18 @@
 //! The virtual memory representation of the MIR interpreter.
 
+mod init_mask;
+mod provenance_map;
+#[cfg(test)]
+mod tests;
+
 use std::borrow::Cow;
-use std::convert::{TryFrom, TryInto};
 use std::fmt;
 use std::hash;
-use std::iter;
-use std::ops::{Deref, Range};
+use std::ops::Range;
 use std::ptr;
 
 use rustc_ast::Mutability;
 use rustc_data_structures::intern::Interned;
-use rustc_data_structures::sorted_map::SortedMap;
 use rustc_span::DUMMY_SP;
 use rustc_target::abi::{Align, HasDataLayout, Size};
 
@@ -20,6 +22,10 @@ use super::{
     UnsupportedOpInfo,
 };
 use crate::ty;
+use init_mask::*;
+use provenance_map::*;
+
+pub use init_mask::{InitChunk, InitChunkIter};
 
 /// This type represents an Allocation in the Miri/CTFE core engine.
 ///
@@ -28,9 +34,9 @@ use crate::ty;
 /// module provides higher-level access.
 // Note: for performance reasons when interning, some of the `Allocation` fields can be partially
 // hashed. (see the `Hash` impl below for more details), so the impl is not derived.
-#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
 #[derive(HashStable)]
-pub struct Allocation<Prov = AllocId, Extra = ()> {
+pub struct Allocation<Prov: Provenance = AllocId, Extra = ()> {
     /// The actual bytes of the allocation.
     /// Note that the bytes of a pointer represent the offset of the pointer.
     bytes: Box<[u8]>,
@@ -102,20 +108,18 @@ impl hash::Hash for Allocation {
 /// (`ConstAllocation`) are used quite a bit.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
 #[rustc_pass_by_value]
-pub struct ConstAllocation<'tcx, Prov = AllocId, Extra = ()>(
-    pub Interned<'tcx, Allocation<Prov, Extra>>,
-);
+pub struct ConstAllocation<'tcx>(pub Interned<'tcx, Allocation>);
 
 impl<'tcx> fmt::Debug for ConstAllocation<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // This matches how `Allocation` is printed. We print it like this to
-        // avoid having to update expected output in a lot of tests.
-        write!(f, "{:?}", self.inner())
+        // The debug representation of this is very verbose and basically useless,
+        // so don't print it.
+        write!(f, "ConstAllocation {{ .. }}")
     }
 }
 
-impl<'tcx, Prov, Extra> ConstAllocation<'tcx, Prov, Extra> {
-    pub fn inner(self) -> &'tcx Allocation<Prov, Extra> {
+impl<'tcx> ConstAllocation<'tcx> {
+    pub fn inner(self) -> &'tcx Allocation {
         self.0.0
     }
 }
@@ -183,12 +187,21 @@ pub fn alloc_range(start: Size, size: Size) -> AllocRange {
     AllocRange { start, size }
 }
 
-impl AllocRange {
+impl From<Range<Size>> for AllocRange {
     #[inline]
-    pub fn from(r: Range<Size>) -> Self {
+    fn from(r: Range<Size>) -> Self {
         alloc_range(r.start, r.end - r.start) // `Size` subtraction (overflow-checked)
     }
+}
+
+impl From<Range<usize>> for AllocRange {
+    #[inline]
+    fn from(r: Range<usize>) -> Self {
+        AllocRange::from(Size::from_bytes(r.start)..Size::from_bytes(r.end))
+    }
+}
 
+impl AllocRange {
     #[inline(always)]
     pub fn end(self) -> Size {
         self.start + self.size // This does overflow checking.
@@ -205,7 +218,7 @@ impl AllocRange {
 }
 
 // The constructors are all without extra; the extra gets added by a machine hook later.
-impl<Prov> Allocation<Prov> {
+impl<Prov: Provenance> Allocation<Prov> {
     /// Creates an allocation initialized by the given bytes
     pub fn from_bytes<'a>(
         slice: impl Into<Cow<'a, [u8]>>,
@@ -263,7 +276,7 @@ impl<Prov> Allocation<Prov> {
 impl Allocation {
     /// Adjust allocation from the ones in tcx to a custom Machine instance
     /// with a different Provenance and Extra type.
-    pub fn adjust_from_tcx<Prov, Extra, Err>(
+    pub fn adjust_from_tcx<Prov: Provenance, Extra, Err>(
         self,
         cx: &impl HasDataLayout,
         extra: Extra,
@@ -271,10 +284,10 @@ impl Allocation {
     ) -> Result<Allocation<Prov, Extra>, Err> {
         // Compute new pointer provenance, which also adjusts the bytes.
         let mut bytes = self.bytes;
-        let mut new_provenance = Vec::with_capacity(self.provenance.0.len());
+        let mut new_provenance = Vec::with_capacity(self.provenance.ptrs().len());
         let ptr_size = cx.data_layout().pointer_size.bytes_usize();
         let endian = cx.data_layout().endian;
-        for &(offset, alloc_id) in self.provenance.iter() {
+        for &(offset, alloc_id) in self.provenance.ptrs().iter() {
             let idx = offset.bytes_usize();
             let ptr_bytes = &mut bytes[idx..idx + ptr_size];
             let bits = read_target_uint(endian, ptr_bytes).unwrap();
@@ -286,7 +299,7 @@ impl Allocation {
         // Create allocation.
         Ok(Allocation {
             bytes,
-            provenance: ProvenanceMap::from_presorted(new_provenance),
+            provenance: ProvenanceMap::from_presorted_ptrs(new_provenance),
             init_mask: self.init_mask,
             align: self.align,
             mutability: self.mutability,
@@ -296,7 +309,7 @@ impl Allocation {
 }
 
 /// Raw accessors. Provide access to otherwise private bytes.
-impl<Prov, Extra> Allocation<Prov, Extra> {
+impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
     pub fn len(&self) -> usize {
         self.bytes.len()
     }
@@ -349,9 +362,14 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
         cx: &impl HasDataLayout,
         range: AllocRange,
     ) -> AllocResult<&[u8]> {
-        self.check_init(range)?;
+        self.init_mask.is_range_initialized(range).map_err(|uninit_range| {
+            AllocError::InvalidUninitBytes(Some(UninitBytesAccess {
+                access: range,
+                uninit: uninit_range,
+            }))
+        })?;
         if !Prov::OFFSET_IS_ADDR {
-            if self.range_has_provenance(cx, range) {
+            if !self.provenance.range_empty(range, cx) {
                 return Err(AllocError::ReadPointerAsBytes);
             }
         }
@@ -370,7 +388,7 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
         range: AllocRange,
     ) -> AllocResult<&mut [u8]> {
         self.mark_init(range, true);
-        self.clear_provenance(cx, range)?;
+        self.provenance.clear(range, cx)?;
 
         Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()])
     }
@@ -382,7 +400,7 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
         range: AllocRange,
     ) -> AllocResult<*mut [u8]> {
         self.mark_init(range, true);
-        self.clear_provenance(cx, range)?;
+        self.provenance.clear(range, cx)?;
 
         assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check
         let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize());
@@ -393,6 +411,15 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
 
 /// Reading and writing.
 impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
+    /// Sets the init bit for the given range.
+    fn mark_init(&mut self, range: AllocRange, is_init: bool) {
+        if range.size.bytes() == 0 {
+            return;
+        }
+        assert!(self.mutability == Mutability::Mut);
+        self.init_mask.set_range(range, is_init);
+    }
+
     /// Reads a *non-ZST* scalar.
     ///
     /// If `read_provenance` is `true`, this will also read provenance; otherwise (if the machine
@@ -410,7 +437,7 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
         read_provenance: bool,
     ) -> AllocResult<Scalar<Prov>> {
         // First and foremost, if anything is uninit, bail.
-        if self.is_init(range).is_err() {
+        if self.init_mask.is_range_initialized(range).is_err() {
             return Err(AllocError::InvalidUninitBytes(None));
         }
 
@@ -423,7 +450,7 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
 
             // When reading data with provenance, the easy case is finding provenance exactly where we
             // are reading, then we can put data and provenance back together and return that.
-            if let Some(&prov) = self.provenance.get(&range.start) {
+            if let Some(prov) = self.provenance.get_ptr(range.start) {
                 // Now we can return the bits, with their appropriate provenance.
                 let ptr = Pointer::new(prov, Size::from_bytes(bits));
                 return Ok(Scalar::from_pointer(ptr, cx));
@@ -431,10 +458,9 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
 
             // If we can work on pointers byte-wise, join the byte-wise provenances.
             if Prov::OFFSET_IS_ADDR {
-                let mut prov = self.offset_get_provenance(cx, range.start);
-                for offset in 1..range.size.bytes() {
-                    let this_prov =
-                        self.offset_get_provenance(cx, range.start + Size::from_bytes(offset));
+                let mut prov = self.provenance.get(range.start, cx);
+                for offset in Size::from_bytes(1)..range.size {
+                    let this_prov = self.provenance.get(range.start + offset, cx);
                     prov = Prov::join(prov, this_prov);
                 }
                 // Now use this provenance.
@@ -452,7 +478,7 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
 
         // Fallback path for when we cannot treat provenance bytewise or ignore it.
         assert!(!Prov::OFFSET_IS_ADDR);
-        if self.range_has_provenance(cx, range) {
+        if !self.provenance.range_empty(range, cx) {
             return Err(AllocError::ReadPointerAsBytes);
         }
         // There is no provenance, we can just return the bits.
@@ -466,7 +492,6 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
     ///
     /// It is the caller's responsibility to check bounds and alignment beforehand.
     /// Most likely, you want to call `InterpCx::write_scalar` instead of this method.
-    #[instrument(skip(self, cx), level = "debug")]
     pub fn write_scalar(
         &mut self,
         cx: &impl HasDataLayout,
@@ -491,7 +516,8 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
 
         // See if we have to also store some provenance.
         if let Some(provenance) = provenance {
-            self.provenance.0.insert(range.start, provenance);
+            assert_eq!(range.size, cx.data_layout().pointer_size);
+            self.provenance.insert_ptr(range.start, provenance, cx);
         }
 
         Ok(())
@@ -500,750 +526,25 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
     /// Write "uninit" to the given memory range.
     pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
         self.mark_init(range, false);
-        self.clear_provenance(cx, range)?;
+        self.provenance.clear(range, cx)?;
         return Ok(());
     }
-}
-
-/// Provenance.
-impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
-    /// Returns all provenance overlapping with the given pointer-offset pair.
-    fn range_get_provenance(&self, cx: &impl HasDataLayout, range: AllocRange) -> &[(Size, Prov)] {
-        // We have to go back `pointer_size - 1` bytes, as that one would still overlap with
-        // the beginning of this range.
-        let start = range.start.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1);
-        self.provenance.range(Size::from_bytes(start)..range.end())
-    }
-
-    /// Get the provenance of a single byte.
-    fn offset_get_provenance(&self, cx: &impl HasDataLayout, offset: Size) -> Option<Prov> {
-        let prov = self.range_get_provenance(cx, alloc_range(offset, Size::from_bytes(1)));
-        assert!(prov.len() <= 1);
-        prov.first().map(|(_offset, prov)| *prov)
-    }
-
-    /// Returns whether this allocation has progrnance overlapping with the given range.
-    ///
-    /// Note: this function exists to allow `range_get_provenance` to be private, in order to somewhat
-    /// limit access to provenance outside of the `Allocation` abstraction.
-    ///
-    pub fn range_has_provenance(&self, cx: &impl HasDataLayout, range: AllocRange) -> bool {
-        !self.range_get_provenance(cx, range).is_empty()
-    }
-
-    /// Removes all provenance inside the given range.
-    /// If there is provenance overlapping with the edges, it
-    /// are removed as well *and* the bytes they cover are marked as
-    /// uninitialized. This is a somewhat odd "spooky action at a distance",
-    /// but it allows strictly more code to run than if we would just error
-    /// immediately in that case.
-    fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult
-    where
-        Prov: Provenance,
-    {
-        // Find the start and end of the given range and its outermost provenance.
-        let (first, last) = {
-            // Find all provenance overlapping the given range.
-            let provenance = self.range_get_provenance(cx, range);
-            if provenance.is_empty() {
-                return Ok(());
-            }
-
-            (
-                provenance.first().unwrap().0,
-                provenance.last().unwrap().0 + cx.data_layout().pointer_size,
-            )
-        };
-        let start = range.start;
-        let end = range.end();
-
-        // We need to handle clearing the provenance from parts of a pointer.
-        // FIXME: Miri should preserve partial provenance; see
-        // https://github.com/rust-lang/miri/issues/2181.
-        if first < start {
-            if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE {
-                return Err(AllocError::PartialPointerOverwrite(first));
-            }
-            warn!(
-                "Partial pointer overwrite! De-initializing memory at offsets {first:?}..{start:?}."
-            );
-            self.init_mask.set_range(first, start, false);
-        }
-        if last > end {
-            if Prov::ERR_ON_PARTIAL_PTR_OVERWRITE {
-                return Err(AllocError::PartialPointerOverwrite(
-                    last - cx.data_layout().pointer_size,
-                ));
-            }
-            warn!(
-                "Partial pointer overwrite! De-initializing memory at offsets {end:?}..{last:?}."
-            );
-            self.init_mask.set_range(end, last, false);
-        }
-
-        // Forget all the provenance.
-        // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine,
-        // i.e., this will not remove any other provenance just after the ones we care about.
-        self.provenance.0.remove_range(first..last);
-
-        Ok(())
-    }
-}
-
-/// Stores the provenance information of pointers stored in memory.
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
-pub struct ProvenanceMap<Prov = AllocId>(SortedMap<Size, Prov>);
-
-impl<Prov> ProvenanceMap<Prov> {
-    pub fn new() -> Self {
-        ProvenanceMap(SortedMap::new())
-    }
-
-    // The caller must guarantee that the given provenance list is already sorted
-    // by address and contain no duplicates.
-    pub fn from_presorted(r: Vec<(Size, Prov)>) -> Self {
-        ProvenanceMap(SortedMap::from_presorted_elements(r))
-    }
-}
-
-impl<Prov> Deref for ProvenanceMap<Prov> {
-    type Target = SortedMap<Size, Prov>;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-/// A partial, owned list of provenance to transfer into another allocation.
-///
-/// Offsets are already adjusted to the destination allocation.
-pub struct AllocationProvenance<Prov> {
-    dest_provenance: Vec<(Size, Prov)>,
-}
-
-impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
-    pub fn prepare_provenance_copy(
-        &self,
-        cx: &impl HasDataLayout,
-        src: AllocRange,
-        dest: Size,
-        count: u64,
-    ) -> AllocationProvenance<Prov> {
-        let provenance = self.range_get_provenance(cx, src);
-        if provenance.is_empty() {
-            return AllocationProvenance { dest_provenance: Vec::new() };
-        }
-
-        let size = src.size;
-        let mut new_provenance = Vec::with_capacity(provenance.len() * (count as usize));
-
-        // If `count` is large, this is rather wasteful -- we are allocating a big array here, which
-        // is mostly filled with redundant information since it's just N copies of the same `Prov`s
-        // at slightly adjusted offsets. The reason we do this is so that in `mark_provenance_range`
-        // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces
-        // the right sequence of provenance for all N copies.
-        for i in 0..count {
-            new_provenance.extend(provenance.iter().map(|&(offset, reloc)| {
-                // compute offset for current repetition
-                let dest_offset = dest + size * i; // `Size` operations
-                (
-                    // shift offsets from source allocation to destination allocation
-                    (offset + dest_offset) - src.start, // `Size` operations
-                    reloc,
-                )
-            }));
-        }
-
-        AllocationProvenance { dest_provenance: new_provenance }
-    }
 
-    /// Applies a provenance copy.
-    /// The affected range, as defined in the parameters to `prepare_provenance_copy` is expected
+    /// Applies a previously prepared provenance copy.
+    /// The affected range, as defined in the parameters to `provenance().prepare_copy` is expected
     /// to be clear of provenance.
     ///
     /// This is dangerous to use as it can violate internal `Allocation` invariants!
     /// It only exists to support an efficient implementation of `mem_copy_repeatedly`.
-    pub fn mark_provenance_range(&mut self, provenance: AllocationProvenance<Prov>) {
-        self.provenance.0.insert_presorted(provenance.dest_provenance);
+    pub fn provenance_apply_copy(&mut self, copy: ProvenanceCopy<Prov>) {
+        self.provenance.apply_copy(copy)
     }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Uninitialized byte tracking
-////////////////////////////////////////////////////////////////////////////////
-
-type Block = u64;
-
-/// A bitmask where each bit refers to the byte with the same index. If the bit is `true`, the byte
-/// is initialized. If it is `false` the byte is uninitialized.
-// Note: for performance reasons when interning, some of the `InitMask` fields can be partially
-// hashed. (see the `Hash` impl below for more details), so the impl is not derived.
-#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
-#[derive(HashStable)]
-pub struct InitMask {
-    blocks: Vec<Block>,
-    len: Size,
-}
-
-// Const allocations are only hashed for interning. However, they can be large, making the hashing
-// expensive especially since it uses `FxHash`: it's better suited to short keys, not potentially
-// big buffers like the allocation's init mask. We can partially hash some fields when they're
-// large.
-impl hash::Hash for InitMask {
-    fn hash<H: hash::Hasher>(&self, state: &mut H) {
-        const MAX_BLOCKS_TO_HASH: usize = MAX_BYTES_TO_HASH / std::mem::size_of::<Block>();
-        const MAX_BLOCKS_LEN: usize = MAX_HASHED_BUFFER_LEN / std::mem::size_of::<Block>();
 
-        // Partially hash the `blocks` buffer when it is large. To limit collisions with common
-        // prefixes and suffixes, we hash the length and some slices of the buffer.
-        let block_count = self.blocks.len();
-        if block_count > MAX_BLOCKS_LEN {
-            // Hash the buffer's length.
-            block_count.hash(state);
-
-            // And its head and tail.
-            self.blocks[..MAX_BLOCKS_TO_HASH].hash(state);
-            self.blocks[block_count - MAX_BLOCKS_TO_HASH..].hash(state);
-        } else {
-            self.blocks.hash(state);
-        }
-
-        // Hash the other fields as usual.
-        self.len.hash(state);
-    }
-}
-
-impl InitMask {
-    pub const BLOCK_SIZE: u64 = 64;
-
-    #[inline]
-    fn bit_index(bits: Size) -> (usize, usize) {
-        // BLOCK_SIZE is the number of bits that can fit in a `Block`.
-        // Each bit in a `Block` represents the initialization state of one byte of an allocation,
-        // so we use `.bytes()` here.
-        let bits = bits.bytes();
-        let a = bits / InitMask::BLOCK_SIZE;
-        let b = bits % InitMask::BLOCK_SIZE;
-        (usize::try_from(a).unwrap(), usize::try_from(b).unwrap())
-    }
-
-    #[inline]
-    fn size_from_bit_index(block: impl TryInto<u64>, bit: impl TryInto<u64>) -> Size {
-        let block = block.try_into().ok().unwrap();
-        let bit = bit.try_into().ok().unwrap();
-        Size::from_bytes(block * InitMask::BLOCK_SIZE + bit)
-    }
-
-    pub fn new(size: Size, state: bool) -> Self {
-        let mut m = InitMask { blocks: vec![], len: Size::ZERO };
-        m.grow(size, state);
-        m
-    }
-
-    pub fn set_range(&mut self, start: Size, end: Size, new_state: bool) {
-        let len = self.len;
-        if end > len {
-            self.grow(end - len, new_state);
-        }
-        self.set_range_inbounds(start, end, new_state);
-    }
-
-    pub fn set_range_inbounds(&mut self, start: Size, end: Size, new_state: bool) {
-        let (blocka, bita) = Self::bit_index(start);
-        let (blockb, bitb) = Self::bit_index(end);
-        if blocka == blockb {
-            // First set all bits except the first `bita`,
-            // then unset the last `64 - bitb` bits.
-            let range = if bitb == 0 {
-                u64::MAX << bita
-            } else {
-                (u64::MAX << bita) & (u64::MAX >> (64 - bitb))
-            };
-            if new_state {
-                self.blocks[blocka] |= range;
-            } else {
-                self.blocks[blocka] &= !range;
-            }
-            return;
-        }
-        // across block boundaries
-        if new_state {
-            // Set `bita..64` to `1`.
-            self.blocks[blocka] |= u64::MAX << bita;
-            // Set `0..bitb` to `1`.
-            if bitb != 0 {
-                self.blocks[blockb] |= u64::MAX >> (64 - bitb);
-            }
-            // Fill in all the other blocks (much faster than one bit at a time).
-            for block in (blocka + 1)..blockb {
-                self.blocks[block] = u64::MAX;
-            }
-        } else {
-            // Set `bita..64` to `0`.
-            self.blocks[blocka] &= !(u64::MAX << bita);
-            // Set `0..bitb` to `0`.
-            if bitb != 0 {
-                self.blocks[blockb] &= !(u64::MAX >> (64 - bitb));
-            }
-            // Fill in all the other blocks (much faster than one bit at a time).
-            for block in (blocka + 1)..blockb {
-                self.blocks[block] = 0;
-            }
-        }
-    }
-
-    #[inline]
-    pub fn get(&self, i: Size) -> bool {
-        let (block, bit) = Self::bit_index(i);
-        (self.blocks[block] & (1 << bit)) != 0
-    }
-
-    #[inline]
-    pub fn set(&mut self, i: Size, new_state: bool) {
-        let (block, bit) = Self::bit_index(i);
-        self.set_bit(block, bit, new_state);
-    }
-
-    #[inline]
-    fn set_bit(&mut self, block: usize, bit: usize, new_state: bool) {
-        if new_state {
-            self.blocks[block] |= 1 << bit;
-        } else {
-            self.blocks[block] &= !(1 << bit);
-        }
-    }
-
-    pub fn grow(&mut self, amount: Size, new_state: bool) {
-        if amount.bytes() == 0 {
-            return;
-        }
-        let unused_trailing_bits =
-            u64::try_from(self.blocks.len()).unwrap() * Self::BLOCK_SIZE - self.len.bytes();
-        if amount.bytes() > unused_trailing_bits {
-            let additional_blocks = amount.bytes() / Self::BLOCK_SIZE + 1;
-            self.blocks.extend(
-                // FIXME(oli-obk): optimize this by repeating `new_state as Block`.
-                iter::repeat(0).take(usize::try_from(additional_blocks).unwrap()),
-            );
-        }
-        let start = self.len;
-        self.len += amount;
-        self.set_range_inbounds(start, start + amount, new_state); // `Size` operation
-    }
-
-    /// Returns the index of the first bit in `start..end` (end-exclusive) that is equal to is_init.
-    fn find_bit(&self, start: Size, end: Size, is_init: bool) -> Option<Size> {
-        /// A fast implementation of `find_bit`,
-        /// which skips over an entire block at a time if it's all 0s (resp. 1s),
-        /// and finds the first 1 (resp. 0) bit inside a block using `trailing_zeros` instead of a loop.
-        ///
-        /// Note that all examples below are written with 8 (instead of 64) bit blocks for simplicity,
-        /// and with the least significant bit (and lowest block) first:
-        /// ```text
-        ///        00000000|00000000
-        ///        ^      ^ ^      ^
-        /// index: 0      7 8      15
-        /// ```
-        /// Also, if not stated, assume that `is_init = true`, that is, we are searching for the first 1 bit.
-        fn find_bit_fast(
-            init_mask: &InitMask,
-            start: Size,
-            end: Size,
-            is_init: bool,
-        ) -> Option<Size> {
-            /// Search one block, returning the index of the first bit equal to `is_init`.
-            fn search_block(
-                bits: Block,
-                block: usize,
-                start_bit: usize,
-                is_init: bool,
-            ) -> Option<Size> {
-                // For the following examples, assume this function was called with:
-                //   bits = 0b00111011
-                //   start_bit = 3
-                //   is_init = false
-                // Note that, for the examples in this function, the most significant bit is written first,
-                // which is backwards compared to the comments in `find_bit`/`find_bit_fast`.
-
-                // Invert bits so we're always looking for the first set bit.
-                //        ! 0b00111011
-                //   bits = 0b11000100
-                let bits = if is_init { bits } else { !bits };
-                // Mask off unused start bits.
-                //          0b11000100
-                //        & 0b11111000
-                //   bits = 0b11000000
-                let bits = bits & (!0 << start_bit);
-                // Find set bit, if any.
-                //   bit = trailing_zeros(0b11000000)
-                //   bit = 6
-                if bits == 0 {
-                    None
-                } else {
-                    let bit = bits.trailing_zeros();
-                    Some(InitMask::size_from_bit_index(block, bit))
-                }
-            }
-
-            if start >= end {
-                return None;
-            }
-
-            // Convert `start` and `end` to block indexes and bit indexes within each block.
-            // We must convert `end` to an inclusive bound to handle block boundaries correctly.
-            //
-            // For example:
-            //
-            //   (a) 00000000|00000000    (b) 00000000|
-            //       ^~~~~~~~~~~^             ^~~~~~~~~^
-            //     start       end          start     end
-            //
-            // In both cases, the block index of `end` is 1.
-            // But we do want to search block 1 in (a), and we don't in (b).
-            //
-            // We subtract 1 from both end positions to make them inclusive:
-            //
-            //   (a) 00000000|00000000    (b) 00000000|
-            //       ^~~~~~~~~~^              ^~~~~~~^
-            //     start    end_inclusive   start end_inclusive
-            //
-            // For (a), the block index of `end_inclusive` is 1, and for (b), it's 0.
-            // This provides the desired behavior of searching blocks 0 and 1 for (a),
-            // and searching only block 0 for (b).
-            // There is no concern of overflows since we checked for `start >= end` above.
-            let (start_block, start_bit) = InitMask::bit_index(start);
-            let end_inclusive = Size::from_bytes(end.bytes() - 1);
-            let (end_block_inclusive, _) = InitMask::bit_index(end_inclusive);
-
-            // Handle first block: need to skip `start_bit` bits.
-            //
-            // We need to handle the first block separately,
-            // because there may be bits earlier in the block that should be ignored,
-            // such as the bit marked (1) in this example:
-            //
-            //       (1)
-            //       -|------
-            //   (c) 01000000|00000000|00000001
-            //          ^~~~~~~~~~~~~~~~~~^
-            //        start              end
-            if let Some(i) =
-                search_block(init_mask.blocks[start_block], start_block, start_bit, is_init)
-            {
-                // If the range is less than a block, we may find a matching bit after `end`.
-                //
-                // For example, we shouldn't successfully find bit (2), because it's after `end`:
-                //
-                //             (2)
-                //       -------|
-                //   (d) 00000001|00000000|00000001
-                //        ^~~~~^
-                //      start end
-                //
-                // An alternative would be to mask off end bits in the same way as we do for start bits,
-                // but performing this check afterwards is faster and simpler to implement.
-                if i < end {
-                    return Some(i);
-                } else {
-                    return None;
-                }
-            }
-
-            // Handle remaining blocks.
-            //
-            // We can skip over an entire block at once if it's all 0s (resp. 1s).
-            // The block marked (3) in this example is the first block that will be handled by this loop,
-            // and it will be skipped for that reason:
-            //
-            //                   (3)
-            //                --------
-            //   (e) 01000000|00000000|00000001
-            //          ^~~~~~~~~~~~~~~~~~^
-            //        start              end
-            if start_block < end_block_inclusive {
-                // This loop is written in a specific way for performance.
-                // Notably: `..end_block_inclusive + 1` is used for an inclusive range instead of `..=end_block_inclusive`,
-                // and `.zip(start_block + 1..)` is used to track the index instead of `.enumerate().skip().take()`,
-                // because both alternatives result in significantly worse codegen.
-                // `end_block_inclusive + 1` is guaranteed not to wrap, because `end_block_inclusive <= end / BLOCK_SIZE`,
-                // and `BLOCK_SIZE` (the number of bits per block) will always be at least 8 (1 byte).
-                for (&bits, block) in init_mask.blocks[start_block + 1..end_block_inclusive + 1]
-                    .iter()
-                    .zip(start_block + 1..)
-                {
-                    if let Some(i) = search_block(bits, block, 0, is_init) {
-                        // If this is the last block, we may find a matching bit after `end`.
-                        //
-                        // For example, we shouldn't successfully find bit (4), because it's after `end`:
-                        //
-                        //                               (4)
-                        //                         -------|
-                        //   (f) 00000001|00000000|00000001
-                        //          ^~~~~~~~~~~~~~~~~~^
-                        //        start              end
-                        //
-                        // As above with example (d), we could handle the end block separately and mask off end bits,
-                        // but unconditionally searching an entire block at once and performing this check afterwards
-                        // is faster and much simpler to implement.
-                        if i < end {
-                            return Some(i);
-                        } else {
-                            return None;
-                        }
-                    }
-                }
-            }
-
-            None
-        }
-
-        #[cfg_attr(not(debug_assertions), allow(dead_code))]
-        fn find_bit_slow(
-            init_mask: &InitMask,
-            start: Size,
-            end: Size,
-            is_init: bool,
-        ) -> Option<Size> {
-            (start..end).find(|&i| init_mask.get(i) == is_init)
-        }
-
-        let result = find_bit_fast(self, start, end, is_init);
-
-        debug_assert_eq!(
-            result,
-            find_bit_slow(self, start, end, is_init),
-            "optimized implementation of find_bit is wrong for start={:?} end={:?} is_init={} init_mask={:#?}",
-            start,
-            end,
-            is_init,
-            self
-        );
-
-        result
-    }
-}
-
-/// A contiguous chunk of initialized or uninitialized memory.
-pub enum InitChunk {
-    Init(Range<Size>),
-    Uninit(Range<Size>),
-}
-
-impl InitChunk {
-    #[inline]
-    pub fn is_init(&self) -> bool {
-        match self {
-            Self::Init(_) => true,
-            Self::Uninit(_) => false,
-        }
-    }
-
-    #[inline]
-    pub fn range(&self) -> Range<Size> {
-        match self {
-            Self::Init(r) => r.clone(),
-            Self::Uninit(r) => r.clone(),
-        }
-    }
-}
-
-impl InitMask {
-    /// Checks whether the range `start..end` (end-exclusive) is entirely initialized.
-    ///
-    /// Returns `Ok(())` if it's initialized. Otherwise returns a range of byte
-    /// indexes for the first contiguous span of the uninitialized access.
-    #[inline]
-    pub fn is_range_initialized(&self, start: Size, end: Size) -> Result<(), AllocRange> {
-        if end > self.len {
-            return Err(AllocRange::from(self.len..end));
-        }
-
-        let uninit_start = self.find_bit(start, end, false);
-
-        match uninit_start {
-            Some(uninit_start) => {
-                let uninit_end = self.find_bit(uninit_start, end, true).unwrap_or(end);
-                Err(AllocRange::from(uninit_start..uninit_end))
-            }
-            None => Ok(()),
-        }
-    }
-
-    /// Returns an iterator, yielding a range of byte indexes for each contiguous region
-    /// of initialized or uninitialized bytes inside the range `start..end` (end-exclusive).
-    ///
-    /// The iterator guarantees the following:
-    /// - Chunks are nonempty.
-    /// - Chunks are adjacent (each range's start is equal to the previous range's end).
-    /// - Chunks span exactly `start..end` (the first starts at `start`, the last ends at `end`).
-    /// - Chunks alternate between [`InitChunk::Init`] and [`InitChunk::Uninit`].
-    #[inline]
-    pub fn range_as_init_chunks(&self, start: Size, end: Size) -> InitChunkIter<'_> {
-        assert!(end <= self.len);
-
-        let is_init = if start < end {
-            self.get(start)
-        } else {
-            // `start..end` is empty: there are no chunks, so use some arbitrary value
-            false
-        };
-
-        InitChunkIter { init_mask: self, is_init, start, end }
-    }
-}
-
-/// Yields [`InitChunk`]s. See [`InitMask::range_as_init_chunks`].
-#[derive(Clone)]
-pub struct InitChunkIter<'a> {
-    init_mask: &'a InitMask,
-    /// Whether the next chunk we will return is initialized.
-    /// If there are no more chunks, contains some arbitrary value.
-    is_init: bool,
-    /// The current byte index into `init_mask`.
-    start: Size,
-    /// The end byte index into `init_mask`.
-    end: Size,
-}
-
-impl<'a> Iterator for InitChunkIter<'a> {
-    type Item = InitChunk;
-
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.start >= self.end {
-            return None;
-        }
-
-        let end_of_chunk =
-            self.init_mask.find_bit(self.start, self.end, !self.is_init).unwrap_or(self.end);
-        let range = self.start..end_of_chunk;
-
-        let ret =
-            Some(if self.is_init { InitChunk::Init(range) } else { InitChunk::Uninit(range) });
-
-        self.is_init = !self.is_init;
-        self.start = end_of_chunk;
-
-        ret
-    }
-}
-
-/// Uninitialized bytes.
-impl<Prov: Copy, Extra> Allocation<Prov, Extra> {
-    /// Checks whether the given range  is entirely initialized.
-    ///
-    /// Returns `Ok(())` if it's initialized. Otherwise returns the range of byte
-    /// indexes of the first contiguous uninitialized access.
-    fn is_init(&self, range: AllocRange) -> Result<(), AllocRange> {
-        self.init_mask.is_range_initialized(range.start, range.end()) // `Size` addition
-    }
-
-    /// Checks that a range of bytes is initialized. If not, returns the `InvalidUninitBytes`
-    /// error which will report the first range of bytes which is uninitialized.
-    fn check_init(&self, range: AllocRange) -> AllocResult {
-        self.is_init(range).map_err(|uninit_range| {
-            AllocError::InvalidUninitBytes(Some(UninitBytesAccess {
-                access: range,
-                uninit: uninit_range,
-            }))
-        })
-    }
-
-    fn mark_init(&mut self, range: AllocRange, is_init: bool) {
-        if range.size.bytes() == 0 {
-            return;
-        }
-        assert!(self.mutability == Mutability::Mut);
-        self.init_mask.set_range(range.start, range.end(), is_init);
-    }
-}
-
-/// Run-length encoding of the uninit mask.
-/// Used to copy parts of a mask multiple times to another allocation.
-pub struct InitMaskCompressed {
-    /// Whether the first range is initialized.
-    initial: bool,
-    /// The lengths of ranges that are run-length encoded.
-    /// The initialization state of the ranges alternate starting with `initial`.
-    ranges: smallvec::SmallVec<[u64; 1]>,
-}
-
-impl InitMaskCompressed {
-    pub fn no_bytes_init(&self) -> bool {
-        // The `ranges` are run-length encoded and of alternating initialization state.
-        // So if `ranges.len() > 1` then the second block is an initialized range.
-        !self.initial && self.ranges.len() == 1
-    }
-}
-
-/// Transferring the initialization mask to other allocations.
-impl<Prov, Extra> Allocation<Prov, Extra> {
-    /// Creates a run-length encoding of the initialization mask; panics if range is empty.
-    ///
-    /// This is essentially a more space-efficient version of
-    /// `InitMask::range_as_init_chunks(...).collect::<Vec<_>>()`.
-    pub fn compress_uninit_range(&self, range: AllocRange) -> InitMaskCompressed {
-        // Since we are copying `size` bytes from `src` to `dest + i * size` (`for i in 0..repeat`),
-        // a naive initialization mask copying algorithm would repeatedly have to read the initialization mask from
-        // the source and write it to the destination. Even if we optimized the memory accesses,
-        // we'd be doing all of this `repeat` times.
-        // Therefore we precompute a compressed version of the initialization mask of the source value and
-        // then write it back `repeat` times without computing any more information from the source.
-
-        // A precomputed cache for ranges of initialized / uninitialized bits
-        // 0000010010001110 will become
-        // `[5, 1, 2, 1, 3, 3, 1]`,
-        // where each element toggles the state.
-
-        let mut ranges = smallvec::SmallVec::<[u64; 1]>::new();
-
-        let mut chunks = self.init_mask.range_as_init_chunks(range.start, range.end()).peekable();
-
-        let initial = chunks.peek().expect("range should be nonempty").is_init();
-
-        // Here we rely on `range_as_init_chunks` to yield alternating init/uninit chunks.
-        for chunk in chunks {
-            let len = chunk.range().end.bytes() - chunk.range().start.bytes();
-            ranges.push(len);
-        }
-
-        InitMaskCompressed { ranges, initial }
-    }
-
-    /// Applies multiple instances of the run-length encoding to the initialization mask.
+    /// Applies a previously prepared copy of the init mask.
     ///
     /// This is dangerous to use as it can violate internal `Allocation` invariants!
     /// It only exists to support an efficient implementation of `mem_copy_repeatedly`.
-    pub fn mark_compressed_init_range(
-        &mut self,
-        defined: &InitMaskCompressed,
-        range: AllocRange,
-        repeat: u64,
-    ) {
-        // An optimization where we can just overwrite an entire range of initialization
-        // bits if they are going to be uniformly `1` or `0`.
-        if defined.ranges.len() <= 1 {
-            self.init_mask.set_range_inbounds(
-                range.start,
-                range.start + range.size * repeat, // `Size` operations
-                defined.initial,
-            );
-            return;
-        }
-
-        for mut j in 0..repeat {
-            j *= range.size.bytes();
-            j += range.start.bytes();
-            let mut cur = defined.initial;
-            for range in &defined.ranges {
-                let old_j = j;
-                j += range;
-                self.init_mask.set_range_inbounds(
-                    Size::from_bytes(old_j),
-                    Size::from_bytes(j),
-                    cur,
-                );
-                cur = !cur;
-            }
-        }
+    pub fn init_mask_apply_copy(&mut self, copy: InitCopy, range: AllocRange, repeat: u64) {
+        self.init_mask.apply_copy(copy, range, repeat)
     }
 }
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs
new file mode 100644
index 00000000000..d88a0c19e59
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs
@@ -0,0 +1,530 @@
+use std::hash;
+use std::iter;
+use std::ops::Range;
+
+use rustc_target::abi::Size;
+
+use super::AllocRange;
+
+type Block = u64;
+
+/// A bitmask where each bit refers to the byte with the same index. If the bit is `true`, the byte
+/// is initialized. If it is `false` the byte is uninitialized.
+// Note: for performance reasons when interning, some of the `InitMask` fields can be partially
+// hashed. (see the `Hash` impl below for more details), so the impl is not derived.
+#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(HashStable)]
+pub struct InitMask {
+    blocks: Vec<Block>,
+    len: Size,
+}
+
+// Const allocations are only hashed for interning. However, they can be large, making the hashing
+// expensive especially since it uses `FxHash`: it's better suited to short keys, not potentially
+// big buffers like the allocation's init mask. We can partially hash some fields when they're
+// large.
+impl hash::Hash for InitMask {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        const MAX_BLOCKS_TO_HASH: usize = super::MAX_BYTES_TO_HASH / std::mem::size_of::<Block>();
+        const MAX_BLOCKS_LEN: usize = super::MAX_HASHED_BUFFER_LEN / std::mem::size_of::<Block>();
+
+        // Partially hash the `blocks` buffer when it is large. To limit collisions with common
+        // prefixes and suffixes, we hash the length and some slices of the buffer.
+        let block_count = self.blocks.len();
+        if block_count > MAX_BLOCKS_LEN {
+            // Hash the buffer's length.
+            block_count.hash(state);
+
+            // And its head and tail.
+            self.blocks[..MAX_BLOCKS_TO_HASH].hash(state);
+            self.blocks[block_count - MAX_BLOCKS_TO_HASH..].hash(state);
+        } else {
+            self.blocks.hash(state);
+        }
+
+        // Hash the other fields as usual.
+        self.len.hash(state);
+    }
+}
+
+impl InitMask {
+    pub const BLOCK_SIZE: u64 = 64;
+
+    pub fn new(size: Size, state: bool) -> Self {
+        let mut m = InitMask { blocks: vec![], len: Size::ZERO };
+        m.grow(size, state);
+        m
+    }
+
+    #[inline]
+    fn bit_index(bits: Size) -> (usize, usize) {
+        // BLOCK_SIZE is the number of bits that can fit in a `Block`.
+        // Each bit in a `Block` represents the initialization state of one byte of an allocation,
+        // so we use `.bytes()` here.
+        let bits = bits.bytes();
+        let a = bits / InitMask::BLOCK_SIZE;
+        let b = bits % InitMask::BLOCK_SIZE;
+        (usize::try_from(a).unwrap(), usize::try_from(b).unwrap())
+    }
+
+    #[inline]
+    fn size_from_bit_index(block: impl TryInto<u64>, bit: impl TryInto<u64>) -> Size {
+        let block = block.try_into().ok().unwrap();
+        let bit = bit.try_into().ok().unwrap();
+        Size::from_bytes(block * InitMask::BLOCK_SIZE + bit)
+    }
+
+    /// Checks whether the `range` is entirely initialized.
+    ///
+    /// Returns `Ok(())` if it's initialized. Otherwise returns a range of byte
+    /// indexes for the first contiguous span of the uninitialized access.
+    #[inline]
+    pub fn is_range_initialized(&self, range: AllocRange) -> Result<(), AllocRange> {
+        let end = range.end();
+        if end > self.len {
+            return Err(AllocRange::from(self.len..end));
+        }
+
+        let uninit_start = self.find_bit(range.start, end, false);
+
+        match uninit_start {
+            Some(uninit_start) => {
+                let uninit_end = self.find_bit(uninit_start, end, true).unwrap_or(end);
+                Err(AllocRange::from(uninit_start..uninit_end))
+            }
+            None => Ok(()),
+        }
+    }
+
+    pub fn set_range(&mut self, range: AllocRange, new_state: bool) {
+        let end = range.end();
+        let len = self.len;
+        if end > len {
+            self.grow(end - len, new_state);
+        }
+        self.set_range_inbounds(range.start, end, new_state);
+    }
+
+    fn set_range_inbounds(&mut self, start: Size, end: Size, new_state: bool) {
+        let (blocka, bita) = Self::bit_index(start);
+        let (blockb, bitb) = Self::bit_index(end);
+        if blocka == blockb {
+            // First set all bits except the first `bita`,
+            // then unset the last `64 - bitb` bits.
+            let range = if bitb == 0 {
+                u64::MAX << bita
+            } else {
+                (u64::MAX << bita) & (u64::MAX >> (64 - bitb))
+            };
+            if new_state {
+                self.blocks[blocka] |= range;
+            } else {
+                self.blocks[blocka] &= !range;
+            }
+            return;
+        }
+        // across block boundaries
+        if new_state {
+            // Set `bita..64` to `1`.
+            self.blocks[blocka] |= u64::MAX << bita;
+            // Set `0..bitb` to `1`.
+            if bitb != 0 {
+                self.blocks[blockb] |= u64::MAX >> (64 - bitb);
+            }
+            // Fill in all the other blocks (much faster than one bit at a time).
+            for block in (blocka + 1)..blockb {
+                self.blocks[block] = u64::MAX;
+            }
+        } else {
+            // Set `bita..64` to `0`.
+            self.blocks[blocka] &= !(u64::MAX << bita);
+            // Set `0..bitb` to `0`.
+            if bitb != 0 {
+                self.blocks[blockb] &= !(u64::MAX >> (64 - bitb));
+            }
+            // Fill in all the other blocks (much faster than one bit at a time).
+            for block in (blocka + 1)..blockb {
+                self.blocks[block] = 0;
+            }
+        }
+    }
+
+    #[inline]
+    pub fn get(&self, i: Size) -> bool {
+        let (block, bit) = Self::bit_index(i);
+        (self.blocks[block] & (1 << bit)) != 0
+    }
+
+    fn grow(&mut self, amount: Size, new_state: bool) {
+        if amount.bytes() == 0 {
+            return;
+        }
+        let unused_trailing_bits =
+            u64::try_from(self.blocks.len()).unwrap() * Self::BLOCK_SIZE - self.len.bytes();
+        if amount.bytes() > unused_trailing_bits {
+            let additional_blocks = amount.bytes() / Self::BLOCK_SIZE + 1;
+            self.blocks.extend(
+                // FIXME(oli-obk): optimize this by repeating `new_state as Block`.
+                iter::repeat(0).take(usize::try_from(additional_blocks).unwrap()),
+            );
+        }
+        let start = self.len;
+        self.len += amount;
+        self.set_range_inbounds(start, start + amount, new_state); // `Size` operation
+    }
+
+    /// Returns the index of the first bit in `start..end` (end-exclusive) that is equal to is_init.
+    fn find_bit(&self, start: Size, end: Size, is_init: bool) -> Option<Size> {
+        /// A fast implementation of `find_bit`,
+        /// which skips over an entire block at a time if it's all 0s (resp. 1s),
+        /// and finds the first 1 (resp. 0) bit inside a block using `trailing_zeros` instead of a loop.
+        ///
+        /// Note that all examples below are written with 8 (instead of 64) bit blocks for simplicity,
+        /// and with the least significant bit (and lowest block) first:
+        /// ```text
+        ///        00000000|00000000
+        ///        ^      ^ ^      ^
+        /// index: 0      7 8      15
+        /// ```
+        /// Also, if not stated, assume that `is_init = true`, that is, we are searching for the first 1 bit.
+        fn find_bit_fast(
+            init_mask: &InitMask,
+            start: Size,
+            end: Size,
+            is_init: bool,
+        ) -> Option<Size> {
+            /// Search one block, returning the index of the first bit equal to `is_init`.
+            fn search_block(
+                bits: Block,
+                block: usize,
+                start_bit: usize,
+                is_init: bool,
+            ) -> Option<Size> {
+                // For the following examples, assume this function was called with:
+                //   bits = 0b00111011
+                //   start_bit = 3
+                //   is_init = false
+                // Note that, for the examples in this function, the most significant bit is written first,
+                // which is backwards compared to the comments in `find_bit`/`find_bit_fast`.
+
+                // Invert bits so we're always looking for the first set bit.
+                //        ! 0b00111011
+                //   bits = 0b11000100
+                let bits = if is_init { bits } else { !bits };
+                // Mask off unused start bits.
+                //          0b11000100
+                //        & 0b11111000
+                //   bits = 0b11000000
+                let bits = bits & (!0 << start_bit);
+                // Find set bit, if any.
+                //   bit = trailing_zeros(0b11000000)
+                //   bit = 6
+                if bits == 0 {
+                    None
+                } else {
+                    let bit = bits.trailing_zeros();
+                    Some(InitMask::size_from_bit_index(block, bit))
+                }
+            }
+
+            if start >= end {
+                return None;
+            }
+
+            // Convert `start` and `end` to block indexes and bit indexes within each block.
+            // We must convert `end` to an inclusive bound to handle block boundaries correctly.
+            //
+            // For example:
+            //
+            //   (a) 00000000|00000000    (b) 00000000|
+            //       ^~~~~~~~~~~^             ^~~~~~~~~^
+            //     start       end          start     end
+            //
+            // In both cases, the block index of `end` is 1.
+            // But we do want to search block 1 in (a), and we don't in (b).
+            //
+            // We subtract 1 from both end positions to make them inclusive:
+            //
+            //   (a) 00000000|00000000    (b) 00000000|
+            //       ^~~~~~~~~~^              ^~~~~~~^
+            //     start    end_inclusive   start end_inclusive
+            //
+            // For (a), the block index of `end_inclusive` is 1, and for (b), it's 0.
+            // This provides the desired behavior of searching blocks 0 and 1 for (a),
+            // and searching only block 0 for (b).
+            // There is no concern of overflows since we checked for `start >= end` above.
+            let (start_block, start_bit) = InitMask::bit_index(start);
+            let end_inclusive = Size::from_bytes(end.bytes() - 1);
+            let (end_block_inclusive, _) = InitMask::bit_index(end_inclusive);
+
+            // Handle first block: need to skip `start_bit` bits.
+            //
+            // We need to handle the first block separately,
+            // because there may be bits earlier in the block that should be ignored,
+            // such as the bit marked (1) in this example:
+            //
+            //       (1)
+            //       -|------
+            //   (c) 01000000|00000000|00000001
+            //          ^~~~~~~~~~~~~~~~~~^
+            //        start              end
+            if let Some(i) =
+                search_block(init_mask.blocks[start_block], start_block, start_bit, is_init)
+            {
+                // If the range is less than a block, we may find a matching bit after `end`.
+                //
+                // For example, we shouldn't successfully find bit (2), because it's after `end`:
+                //
+                //             (2)
+                //       -------|
+                //   (d) 00000001|00000000|00000001
+                //        ^~~~~^
+                //      start end
+                //
+                // An alternative would be to mask off end bits in the same way as we do for start bits,
+                // but performing this check afterwards is faster and simpler to implement.
+                if i < end {
+                    return Some(i);
+                } else {
+                    return None;
+                }
+            }
+
+            // Handle remaining blocks.
+            //
+            // We can skip over an entire block at once if it's all 0s (resp. 1s).
+            // The block marked (3) in this example is the first block that will be handled by this loop,
+            // and it will be skipped for that reason:
+            //
+            //                   (3)
+            //                --------
+            //   (e) 01000000|00000000|00000001
+            //          ^~~~~~~~~~~~~~~~~~^
+            //        start              end
+            if start_block < end_block_inclusive {
+                // This loop is written in a specific way for performance.
+                // Notably: `..end_block_inclusive + 1` is used for an inclusive range instead of `..=end_block_inclusive`,
+                // and `.zip(start_block + 1..)` is used to track the index instead of `.enumerate().skip().take()`,
+                // because both alternatives result in significantly worse codegen.
+                // `end_block_inclusive + 1` is guaranteed not to wrap, because `end_block_inclusive <= end / BLOCK_SIZE`,
+                // and `BLOCK_SIZE` (the number of bits per block) will always be at least 8 (1 byte).
+                for (&bits, block) in init_mask.blocks[start_block + 1..end_block_inclusive + 1]
+                    .iter()
+                    .zip(start_block + 1..)
+                {
+                    if let Some(i) = search_block(bits, block, 0, is_init) {
+                        // If this is the last block, we may find a matching bit after `end`.
+                        //
+                        // For example, we shouldn't successfully find bit (4), because it's after `end`:
+                        //
+                        //                               (4)
+                        //                         -------|
+                        //   (f) 00000001|00000000|00000001
+                        //          ^~~~~~~~~~~~~~~~~~^
+                        //        start              end
+                        //
+                        // As above with example (d), we could handle the end block separately and mask off end bits,
+                        // but unconditionally searching an entire block at once and performing this check afterwards
+                        // is faster and much simpler to implement.
+                        if i < end {
+                            return Some(i);
+                        } else {
+                            return None;
+                        }
+                    }
+                }
+            }
+
+            None
+        }
+
+        #[cfg_attr(not(debug_assertions), allow(dead_code))]
+        fn find_bit_slow(
+            init_mask: &InitMask,
+            start: Size,
+            end: Size,
+            is_init: bool,
+        ) -> Option<Size> {
+            (start..end).find(|&i| init_mask.get(i) == is_init)
+        }
+
+        let result = find_bit_fast(self, start, end, is_init);
+
+        debug_assert_eq!(
+            result,
+            find_bit_slow(self, start, end, is_init),
+            "optimized implementation of find_bit is wrong for start={:?} end={:?} is_init={} init_mask={:#?}",
+            start,
+            end,
+            is_init,
+            self
+        );
+
+        result
+    }
+}
+
+/// A contiguous chunk of initialized or uninitialized memory.
+pub enum InitChunk {
+    Init(Range<Size>),
+    Uninit(Range<Size>),
+}
+
+impl InitChunk {
+    #[inline]
+    pub fn is_init(&self) -> bool {
+        match self {
+            Self::Init(_) => true,
+            Self::Uninit(_) => false,
+        }
+    }
+
+    #[inline]
+    pub fn range(&self) -> Range<Size> {
+        match self {
+            Self::Init(r) => r.clone(),
+            Self::Uninit(r) => r.clone(),
+        }
+    }
+}
+
+impl InitMask {
+    /// Returns an iterator, yielding a range of byte indexes for each contiguous region
+    /// of initialized or uninitialized bytes inside the range `start..end` (end-exclusive).
+    ///
+    /// The iterator guarantees the following:
+    /// - Chunks are nonempty.
+    /// - Chunks are adjacent (each range's start is equal to the previous range's end).
+    /// - Chunks span exactly `start..end` (the first starts at `start`, the last ends at `end`).
+    /// - Chunks alternate between [`InitChunk::Init`] and [`InitChunk::Uninit`].
+    #[inline]
+    pub fn range_as_init_chunks(&self, range: AllocRange) -> InitChunkIter<'_> {
+        let start = range.start;
+        let end = range.end();
+        assert!(end <= self.len);
+
+        let is_init = if start < end {
+            self.get(start)
+        } else {
+            // `start..end` is empty: there are no chunks, so use some arbitrary value
+            false
+        };
+
+        InitChunkIter { init_mask: self, is_init, start, end }
+    }
+}
+
+/// Yields [`InitChunk`]s. See [`InitMask::range_as_init_chunks`].
+#[derive(Clone)]
+pub struct InitChunkIter<'a> {
+    init_mask: &'a InitMask,
+    /// Whether the next chunk we will return is initialized.
+    /// If there are no more chunks, contains some arbitrary value.
+    is_init: bool,
+    /// The current byte index into `init_mask`.
+    start: Size,
+    /// The end byte index into `init_mask`.
+    end: Size,
+}
+
+impl<'a> Iterator for InitChunkIter<'a> {
+    type Item = InitChunk;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.start >= self.end {
+            return None;
+        }
+
+        let end_of_chunk =
+            self.init_mask.find_bit(self.start, self.end, !self.is_init).unwrap_or(self.end);
+        let range = self.start..end_of_chunk;
+
+        let ret =
+            Some(if self.is_init { InitChunk::Init(range) } else { InitChunk::Uninit(range) });
+
+        self.is_init = !self.is_init;
+        self.start = end_of_chunk;
+
+        ret
+    }
+}
+
+/// Run-length encoding of the uninit mask.
+/// Used to copy parts of a mask multiple times to another allocation.
+pub struct InitCopy {
+    /// Whether the first range is initialized.
+    initial: bool,
+    /// The lengths of ranges that are run-length encoded.
+    /// The initialization state of the ranges alternate starting with `initial`.
+    ranges: smallvec::SmallVec<[u64; 1]>,
+}
+
+impl InitCopy {
+    pub fn no_bytes_init(&self) -> bool {
+        // The `ranges` are run-length encoded and of alternating initialization state.
+        // So if `ranges.len() > 1` then the second block is an initialized range.
+        !self.initial && self.ranges.len() == 1
+    }
+}
+
+/// Transferring the initialization mask to other allocations.
+impl InitMask {
+    /// Creates a run-length encoding of the initialization mask; panics if range is empty.
+    ///
+    /// This is essentially a more space-efficient version of
+    /// `InitMask::range_as_init_chunks(...).collect::<Vec<_>>()`.
+    pub fn prepare_copy(&self, range: AllocRange) -> InitCopy {
+        // Since we are copying `size` bytes from `src` to `dest + i * size` (`for i in 0..repeat`),
+        // a naive initialization mask copying algorithm would repeatedly have to read the initialization mask from
+        // the source and write it to the destination. Even if we optimized the memory accesses,
+        // we'd be doing all of this `repeat` times.
+        // Therefore we precompute a compressed version of the initialization mask of the source value and
+        // then write it back `repeat` times without computing any more information from the source.
+
+        // A precomputed cache for ranges of initialized / uninitialized bits
+        // 0000010010001110 will become
+        // `[5, 1, 2, 1, 3, 3, 1]`,
+        // where each element toggles the state.
+
+        let mut ranges = smallvec::SmallVec::<[u64; 1]>::new();
+
+        let mut chunks = self.range_as_init_chunks(range).peekable();
+
+        let initial = chunks.peek().expect("range should be nonempty").is_init();
+
+        // Here we rely on `range_as_init_chunks` to yield alternating init/uninit chunks.
+        for chunk in chunks {
+            let len = chunk.range().end.bytes() - chunk.range().start.bytes();
+            ranges.push(len);
+        }
+
+        InitCopy { ranges, initial }
+    }
+
+    /// Applies multiple instances of the run-length encoding to the initialization mask.
+    pub fn apply_copy(&mut self, defined: InitCopy, range: AllocRange, repeat: u64) {
+        // An optimization where we can just overwrite an entire range of initialization
+        // bits if they are going to be uniformly `1` or `0`.
+        if defined.ranges.len() <= 1 {
+            self.set_range_inbounds(
+                range.start,
+                range.start + range.size * repeat, // `Size` operations
+                defined.initial,
+            );
+            return;
+        }
+
+        for mut j in 0..repeat {
+            j *= range.size.bytes();
+            j += range.start.bytes();
+            let mut cur = defined.initial;
+            for range in &defined.ranges {
+                let old_j = j;
+                j += range;
+                self.set_range_inbounds(Size::from_bytes(old_j), Size::from_bytes(j), cur);
+                cur = !cur;
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
new file mode 100644
index 00000000000..19ee209e552
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs
@@ -0,0 +1,321 @@
+//! Store the provenance for each byte in the range, with a more efficient
+//! representation for the common case where PTR_SIZE consecutive bytes have the same provenance.
+
+use std::cmp;
+
+use rustc_data_structures::sorted_map::SortedMap;
+use rustc_target::abi::{HasDataLayout, Size};
+
+use super::{alloc_range, AllocError, AllocId, AllocRange, AllocResult, Provenance};
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+
+/// Stores the provenance information of pointers stored in memory.
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(HashStable)]
+pub struct ProvenanceMap<Prov = AllocId> {
+    /// Provenance in this map applies from the given offset for an entire pointer-size worth of
+    /// bytes. Two entires in this map are always at least a pointer size apart.
+    ptrs: SortedMap<Size, Prov>,
+    /// Provenance in this map only applies to the given single byte.
+    /// This map is disjoint from the previous. It will always be empty when
+    /// `Prov::OFFSET_IS_ADDR` is false.
+    bytes: Option<Box<SortedMap<Size, Prov>>>,
+}
+
+impl<D: Decoder, Prov: Provenance + Decodable<D>> Decodable<D> for ProvenanceMap<Prov> {
+    fn decode(d: &mut D) -> Self {
+        assert!(!Prov::OFFSET_IS_ADDR); // only `AllocId` is ever serialized
+        Self { ptrs: Decodable::decode(d), bytes: None }
+    }
+}
+
+impl<S: Encoder, Prov: Provenance + Encodable<S>> Encodable<S> for ProvenanceMap<Prov> {
+    fn encode(&self, s: &mut S) {
+        let Self { ptrs, bytes } = self;
+        assert!(!Prov::OFFSET_IS_ADDR); // only `AllocId` is ever serialized
+        debug_assert!(bytes.is_none());
+        ptrs.encode(s)
+    }
+}
+
+impl<Prov> ProvenanceMap<Prov> {
+    pub fn new() -> Self {
+        ProvenanceMap { ptrs: SortedMap::new(), bytes: None }
+    }
+
+    /// The caller must guarantee that the given provenance list is already sorted
+    /// by address and contain no duplicates.
+    pub fn from_presorted_ptrs(r: Vec<(Size, Prov)>) -> Self {
+        ProvenanceMap { ptrs: SortedMap::from_presorted_elements(r), bytes: None }
+    }
+}
+
+impl ProvenanceMap {
+    /// Give access to the ptr-sized provenances (which can also be thought of as relocations, and
+    /// indeed that is how codegen treats them).
+    ///
+    /// Only exposed with `AllocId` provenance, since it panics if there is bytewise provenance.
+    #[inline]
+    pub fn ptrs(&self) -> &SortedMap<Size, AllocId> {
+        debug_assert!(self.bytes.is_none()); // `AllocId::OFFSET_IS_ADDR` is false so this cannot fail
+        &self.ptrs
+    }
+}
+
+impl<Prov: Provenance> ProvenanceMap<Prov> {
+    /// Returns all ptr-sized provenance in the given range.
+    /// If the range has length 0, returns provenance that crosses the edge between `start-1` and
+    /// `start`.
+    fn range_get_ptrs(&self, range: AllocRange, cx: &impl HasDataLayout) -> &[(Size, Prov)] {
+        // We have to go back `pointer_size - 1` bytes, as that one would still overlap with
+        // the beginning of this range.
+        let adjusted_start = Size::from_bytes(
+            range.start.bytes().saturating_sub(cx.data_layout().pointer_size.bytes() - 1),
+        );
+        self.ptrs.range(adjusted_start..range.end())
+    }
+
+    /// Returns all byte-wise provenance in the given range.
+    fn range_get_bytes(&self, range: AllocRange) -> &[(Size, Prov)] {
+        if let Some(bytes) = self.bytes.as_ref() {
+            bytes.range(range.start..range.end())
+        } else {
+            &[]
+        }
+    }
+
+    /// Get the provenance of a single byte.
+    pub fn get(&self, offset: Size, cx: &impl HasDataLayout) -> Option<Prov> {
+        let prov = self.range_get_ptrs(alloc_range(offset, Size::from_bytes(1)), cx);
+        debug_assert!(prov.len() <= 1);
+        if let Some(entry) = prov.first() {
+            // If it overlaps with this byte, it is on this byte.
+            debug_assert!(self.bytes.as_ref().map_or(true, |b| b.get(&offset).is_none()));
+            Some(entry.1)
+        } else {
+            // Look up per-byte provenance.
+            self.bytes.as_ref().and_then(|b| b.get(&offset).copied())
+        }
+    }
+
+    /// Check if here is ptr-sized provenance at the given index.
+    /// Does not mean anything for bytewise provenance! But can be useful as an optimization.
+    pub fn get_ptr(&self, offset: Size) -> Option<Prov> {
+        self.ptrs.get(&offset).copied()
+    }
+
+    /// Returns whether this allocation has provenance overlapping with the given range.
+    ///
+    /// Note: this function exists to allow `range_get_provenance` to be private, in order to somewhat
+    /// limit access to provenance outside of the `Allocation` abstraction.
+    ///
+    pub fn range_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool {
+        self.range_get_ptrs(range, cx).is_empty() && self.range_get_bytes(range).is_empty()
+    }
+
+    /// Yields all the provenances stored in this map.
+    pub fn provenances(&self) -> impl Iterator<Item = Prov> + '_ {
+        let bytes = self.bytes.iter().flat_map(|b| b.values());
+        self.ptrs.values().chain(bytes).copied()
+    }
+
+    pub fn insert_ptr(&mut self, offset: Size, prov: Prov, cx: &impl HasDataLayout) {
+        debug_assert!(self.range_empty(alloc_range(offset, cx.data_layout().pointer_size), cx));
+        self.ptrs.insert(offset, prov);
+    }
+
+    /// Removes all provenance inside the given range.
+    /// If there is provenance overlapping with the edges, might result in an error.
+    pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) -> AllocResult {
+        let start = range.start;
+        let end = range.end();
+        // Clear the bytewise part -- this is easy.
+        if Prov::OFFSET_IS_ADDR {
+            if let Some(bytes) = self.bytes.as_mut() {
+                bytes.remove_range(start..end);
+            }
+        } else {
+            debug_assert!(self.bytes.is_none());
+        }
+
+        // For the ptr-sized part, find the first (inclusive) and last (exclusive) byte of
+        // provenance that overlaps with the given range.
+        let (first, last) = {
+            // Find all provenance overlapping the given range.
+            let provenance = self.range_get_ptrs(range, cx);
+            if provenance.is_empty() {
+                // No provenance in this range, we are done.
+                return Ok(());
+            }
+
+            (
+                provenance.first().unwrap().0,
+                provenance.last().unwrap().0 + cx.data_layout().pointer_size,
+            )
+        };
+
+        // We need to handle clearing the provenance from parts of a pointer.
+        if first < start {
+            if !Prov::OFFSET_IS_ADDR {
+                // We can't split up the provenance into less than a pointer.
+                return Err(AllocError::PartialPointerOverwrite(first));
+            }
+            // Insert the remaining part in the bytewise provenance.
+            let prov = self.ptrs[&first];
+            let bytes = self.bytes.get_or_insert_with(Box::default);
+            for offset in first..start {
+                bytes.insert(offset, prov);
+            }
+        }
+        if last > end {
+            let begin_of_last = last - cx.data_layout().pointer_size;
+            if !Prov::OFFSET_IS_ADDR {
+                // We can't split up the provenance into less than a pointer.
+                return Err(AllocError::PartialPointerOverwrite(begin_of_last));
+            }
+            // Insert the remaining part in the bytewise provenance.
+            let prov = self.ptrs[&begin_of_last];
+            let bytes = self.bytes.get_or_insert_with(Box::default);
+            for offset in end..last {
+                bytes.insert(offset, prov);
+            }
+        }
+
+        // Forget all the provenance.
+        // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine,
+        // i.e., this will not remove any other provenance just after the ones we care about.
+        self.ptrs.remove_range(first..last);
+
+        Ok(())
+    }
+}
+
+/// A partial, owned list of provenance to transfer into another allocation.
+///
+/// Offsets are already adjusted to the destination allocation.
+pub struct ProvenanceCopy<Prov> {
+    dest_ptrs: Option<Box<[(Size, Prov)]>>,
+    dest_bytes: Option<Box<[(Size, Prov)]>>,
+}
+
+impl<Prov: Provenance> ProvenanceMap<Prov> {
+    pub fn prepare_copy(
+        &self,
+        src: AllocRange,
+        dest: Size,
+        count: u64,
+        cx: &impl HasDataLayout,
+    ) -> AllocResult<ProvenanceCopy<Prov>> {
+        let shift_offset = move |idx, offset| {
+            // compute offset for current repetition
+            let dest_offset = dest + src.size * idx; // `Size` operations
+            // shift offsets from source allocation to destination allocation
+            (offset - src.start) + dest_offset // `Size` operations
+        };
+        let ptr_size = cx.data_layout().pointer_size;
+
+        // # Pointer-sized provenances
+        // Get the provenances that are entirely within this range.
+        // (Different from `range_get_ptrs` which asks if they overlap the range.)
+        // Only makes sense if we are copying at least one pointer worth of bytes.
+        let mut dest_ptrs_box = None;
+        if src.size >= ptr_size {
+            let adjusted_end = Size::from_bytes(src.end().bytes() - (ptr_size.bytes() - 1));
+            let ptrs = self.ptrs.range(src.start..adjusted_end);
+            // If `count` is large, this is rather wasteful -- we are allocating a big array here, which
+            // is mostly filled with redundant information since it's just N copies of the same `Prov`s
+            // at slightly adjusted offsets. The reason we do this is so that in `mark_provenance_range`
+            // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces
+            // the right sequence of provenance for all N copies.
+            // Basically, this large array would have to be created anyway in the target allocation.
+            let mut dest_ptrs = Vec::with_capacity(ptrs.len() * (count as usize));
+            for i in 0..count {
+                dest_ptrs
+                    .extend(ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)));
+            }
+            debug_assert_eq!(dest_ptrs.len(), dest_ptrs.capacity());
+            dest_ptrs_box = Some(dest_ptrs.into_boxed_slice());
+        };
+
+        // # Byte-sized provenances
+        // This includes the existing bytewise provenance in the range, and ptr provenance
+        // that overlaps with the begin/end of the range.
+        let mut dest_bytes_box = None;
+        let begin_overlap = self.range_get_ptrs(alloc_range(src.start, Size::ZERO), cx).first();
+        let end_overlap = self.range_get_ptrs(alloc_range(src.end(), Size::ZERO), cx).first();
+        if !Prov::OFFSET_IS_ADDR {
+            // There can't be any bytewise provenance, and we cannot split up the begin/end overlap.
+            if let Some(entry) = begin_overlap {
+                return Err(AllocError::PartialPointerCopy(entry.0));
+            }
+            if let Some(entry) = end_overlap {
+                return Err(AllocError::PartialPointerCopy(entry.0));
+            }
+            debug_assert!(self.bytes.is_none());
+        } else {
+            let mut bytes = Vec::new();
+            // First, if there is a part of a pointer at the start, add that.
+            if let Some(entry) = begin_overlap {
+                trace!("start overlapping entry: {entry:?}");
+                // For really small copies, make sure we don't run off the end of the `src` range.
+                let entry_end = cmp::min(entry.0 + ptr_size, src.end());
+                for offset in src.start..entry_end {
+                    bytes.push((offset, entry.1));
+                }
+            } else {
+                trace!("no start overlapping entry");
+            }
+            // Then the main part, bytewise provenance from `self.bytes`.
+            if let Some(all_bytes) = self.bytes.as_ref() {
+                bytes.extend(all_bytes.range(src.start..src.end()));
+            }
+            // And finally possibly parts of a pointer at the end.
+            if let Some(entry) = end_overlap {
+                trace!("end overlapping entry: {entry:?}");
+                // For really small copies, make sure we don't start before `src` does.
+                let entry_start = cmp::max(entry.0, src.start);
+                for offset in entry_start..src.end() {
+                    if bytes.last().map_or(true, |bytes_entry| bytes_entry.0 < offset) {
+                        // The last entry, if it exists, has a lower offset than us.
+                        bytes.push((offset, entry.1));
+                    } else {
+                        // There already is an entry for this offset in there! This can happen when the
+                        // start and end range checks actually end up hitting the same pointer, so we
+                        // already added this in the "pointer at the start" part above.
+                        assert!(entry.0 <= src.start);
+                    }
+                }
+            } else {
+                trace!("no end overlapping entry");
+            }
+            trace!("byte provenances: {bytes:?}");
+
+            // And again a buffer for the new list on the target side.
+            let mut dest_bytes = Vec::with_capacity(bytes.len() * (count as usize));
+            for i in 0..count {
+                dest_bytes
+                    .extend(bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)));
+            }
+            debug_assert_eq!(dest_bytes.len(), dest_bytes.capacity());
+            dest_bytes_box = Some(dest_bytes.into_boxed_slice());
+        }
+
+        Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box })
+    }
+
+    /// Applies a provenance copy.
+    /// The affected range, as defined in the parameters to `prepare_copy` is expected
+    /// to be clear of provenance.
+    pub fn apply_copy(&mut self, copy: ProvenanceCopy<Prov>) {
+        if let Some(dest_ptrs) = copy.dest_ptrs {
+            self.ptrs.insert_presorted(dest_ptrs.into());
+        }
+        if Prov::OFFSET_IS_ADDR {
+            if let Some(dest_bytes) = copy.dest_bytes && !dest_bytes.is_empty() {
+                self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into());
+            }
+        } else {
+            debug_assert!(copy.dest_bytes.is_none());
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/tests.rs b/compiler/rustc_middle/src/mir/interpret/allocation/tests.rs
new file mode 100644
index 00000000000..c9c3c50c537
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/interpret/allocation/tests.rs
@@ -0,0 +1,19 @@
+use super::*;
+
+#[test]
+fn uninit_mask() {
+    let mut mask = InitMask::new(Size::from_bytes(500), false);
+    assert!(!mask.get(Size::from_bytes(499)));
+    mask.set_range(alloc_range(Size::from_bytes(499), Size::from_bytes(1)), true);
+    assert!(mask.get(Size::from_bytes(499)));
+    mask.set_range((100..256).into(), true);
+    for i in 0..100 {
+        assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set");
+    }
+    for i in 100..256 {
+        assert!(mask.get(Size::from_bytes(i)), "{i} should be set");
+    }
+    for i in 256..499 {
+        assert!(!mask.get(Size::from_bytes(i)), "{i} should not be set");
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index b5a50cc1527..ae0dbad8b08 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -16,8 +16,6 @@ pub enum ErrorHandled {
     /// Already reported an error for this evaluation, and the compilation is
     /// *guaranteed* to fail. Warnings/lints *must not* produce `Reported`.
     Reported(ErrorGuaranteed),
-    /// Already emitted a lint for this evaluation.
-    Linted,
     /// Don't emit an error, the evaluation failed because the MIR was generic
     /// and the substs didn't fully monomorphize it.
     TooGeneric,
@@ -89,18 +87,6 @@ fn print_backtrace(backtrace: &Backtrace) {
     eprintln!("\n\nAn error occurred in miri:\n{}", backtrace);
 }
 
-impl From<ErrorHandled> for InterpErrorInfo<'_> {
-    fn from(err: ErrorHandled) -> Self {
-        match err {
-            ErrorHandled::Reported(ErrorGuaranteed { .. }) | ErrorHandled::Linted => {
-                err_inval!(ReferencedConstant)
-            }
-            ErrorHandled::TooGeneric => err_inval!(TooGeneric),
-        }
-        .into()
-    }
-}
-
 impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
     fn from(err: ErrorGuaranteed) -> Self {
         InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into()
@@ -138,9 +124,6 @@ impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
 pub enum InvalidProgramInfo<'tcx> {
     /// Resolution can fail if we are in a too generic context.
     TooGeneric,
-    /// Cannot compute this constant because it depends on another one
-    /// which already produced an error.
-    ReferencedConstant,
     /// Abort in case errors are already reported.
     AlreadyReported(ErrorGuaranteed),
     /// An error occurred during layout computation.
@@ -158,9 +141,11 @@ impl fmt::Display for InvalidProgramInfo<'_> {
         use InvalidProgramInfo::*;
         match self {
             TooGeneric => write!(f, "encountered overly generic constant"),
-            ReferencedConstant => write!(f, "referenced constant has errors"),
             AlreadyReported(ErrorGuaranteed { .. }) => {
-                write!(f, "encountered constants with type errors, stopping evaluation")
+                write!(
+                    f,
+                    "an error has already been reported elsewhere (this sould not usually be printed)"
+                )
             }
             Layout(ref err) => write!(f, "{err}"),
             FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"),
@@ -401,16 +386,15 @@ impl fmt::Display for UndefinedBehaviorInfo {
 pub enum UnsupportedOpInfo {
     /// Free-form case. Only for errors that are never caught!
     Unsupported(String),
-    /// Overwriting parts of a pointer; the resulting state cannot be represented in our
-    /// `Allocation` data structure. See <https://github.com/rust-lang/miri/issues/2181>.
-    PartialPointerOverwrite(Pointer<AllocId>),
-    /// Attempting to `copy` parts of a pointer to somewhere else; the resulting state cannot be
-    /// represented in our `Allocation` data structure. See
-    /// <https://github.com/rust-lang/miri/issues/2181>.
-    PartialPointerCopy(Pointer<AllocId>),
     //
     // The variants below are only reachable from CTFE/const prop, miri will never emit them.
     //
+    /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state
+    /// cannot be represented by the CTFE interpreter.
+    PartialPointerOverwrite(Pointer<AllocId>),
+    /// Attempting to `copy` parts of a pointer to somewhere else; without knowing absolute
+    /// addresses, the resulting state cannot be represented by the CTFE interpreter.
+    PartialPointerCopy(Pointer<AllocId>),
     /// Encountered a pointer where we needed raw bytes.
     ReadPointerAsBytes,
     /// Accessing thread local statics
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 32ec5855769..d79cd8b7a8a 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -128,8 +128,8 @@ pub use self::error::{
 pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
 
 pub use self::allocation::{
-    alloc_range, AllocRange, Allocation, ConstAllocation, InitChunk, InitChunkIter, InitMask,
-    ProvenanceMap,
+    alloc_range, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk,
+    InitChunkIter,
 };
 
 pub use self::pointer::{Pointer, PointerArithmetic, Provenance};
diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs
index 23c2ce6474c..4e59f1b2482 100644
--- a/compiler/rustc_middle/src/mir/interpret/pointer.rs
+++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs
@@ -103,8 +103,7 @@ impl<T: HasDataLayout> PointerArithmetic for T {}
 /// This trait abstracts over the kind of provenance that is associated with a `Pointer`. It is
 /// mostly opaque; the `Machine` trait extends it with some more operations that also have access to
 /// some global state.
-/// We don't actually care about this `Debug` bound (we use `Provenance::fmt` to format the entire
-/// pointer), but `derive` adds some unnecessary bounds.
+/// The `Debug` rendering is used to distplay bare provenance, and for the default impl of `fmt`.
 pub trait Provenance: Copy + fmt::Debug {
     /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address.
     /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are
@@ -115,14 +114,23 @@ pub trait Provenance: Copy + fmt::Debug {
     ///   pointer, and implement ptr-to-int transmutation by stripping provenance.
     const OFFSET_IS_ADDR: bool;
 
-    /// We also use this trait to control whether to abort execution when a pointer is being partially overwritten
-    /// (this avoids a separate trait in `allocation.rs` just for this purpose).
-    const ERR_ON_PARTIAL_PTR_OVERWRITE: bool;
-
     /// Determines how a pointer should be printed.
+    ///
+    /// Default impl is only good for when `OFFSET_IS_ADDR == true`.
     fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result
     where
-        Self: Sized;
+        Self: Sized,
+    {
+        assert!(Self::OFFSET_IS_ADDR);
+        let (prov, addr) = ptr.into_parts(); // address is absolute
+        write!(f, "{:#x}", addr.bytes())?;
+        if f.alternate() {
+            write!(f, "{prov:#?}")?;
+        } else {
+            write!(f, "{prov:?}")?;
+        }
+        Ok(())
+    }
 
     /// If `OFFSET_IS_ADDR == false`, provenance must always be able to
     /// identify the allocation this ptr points to (i.e., this must return `Some`).
@@ -139,9 +147,6 @@ impl Provenance for AllocId {
     // so ptr-to-int casts are not possible (since we do not know the global physical offset).
     const OFFSET_IS_ADDR: bool = false;
 
-    // For now, do not allow this, so that we keep our options open.
-    const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = true;
-
     fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // Forward `alternate` flag to `alloc_id` printing.
         if f.alternate() {
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 473894ac1ca..b6c6e9d559c 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -175,6 +175,8 @@ impl<'tcx> TyCtxt<'tcx> {
 
 impl<'tcx> TyCtxtAt<'tcx> {
     /// Evaluate a static's initializer, returning the allocation of the initializer's memory.
+    ///
+    /// The span is entirely ignored here, but still helpful for better query cycle errors.
     pub fn eval_static_initializer(
         self,
         def_id: DefId,
@@ -187,6 +189,8 @@ impl<'tcx> TyCtxtAt<'tcx> {
     }
 
     /// Evaluate anything constant-like, returning the allocation of the final memory.
+    ///
+    /// The span is entirely ignored here, but still helpful for better query cycle errors.
     fn eval_to_allocation(
         self,
         gid: GlobalId<'tcx>,
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index a4495d2934d..e0a786e201a 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -10,7 +10,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder};
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
 use crate::ty::print::{FmtPrinter, Printer};
 use crate::ty::visit::{TypeVisitable, TypeVisitor};
-use crate::ty::{self, List, Ty, TyCtxt};
+use crate::ty::{self, DefIdTree, List, Ty, TyCtxt};
 use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
 use crate::ty::{GenericArg, InternalSubsts, SubstsRef};
 
@@ -1071,6 +1071,18 @@ pub enum VarDebugInfoContents<'tcx> {
     /// based on a `Local`, not a `Static`, and contains no indexing.
     Place(Place<'tcx>),
     Const(Constant<'tcx>),
+    /// The user variable's data is split across several fragments,
+    /// each described by a `VarDebugInfoFragment`.
+    /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
+    /// and LLVM's `DW_OP_LLVM_fragment` for more details on
+    /// the underlying debuginfo feature this relies on.
+    Composite {
+        /// Type of the original user variable.
+        ty: Ty<'tcx>,
+        /// All the parts of the original user variable, which ended
+        /// up in disjoint places, due to optimizations.
+        fragments: Vec<VarDebugInfoFragment<'tcx>>,
+    },
 }
 
 impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
@@ -1078,7 +1090,48 @@ impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
         match self {
             VarDebugInfoContents::Const(c) => write!(fmt, "{}", c),
             VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p),
+            VarDebugInfoContents::Composite { ty, fragments } => {
+                write!(fmt, "{:?}{{ ", ty)?;
+                for f in fragments.iter() {
+                    write!(fmt, "{:?}, ", f)?;
+                }
+                write!(fmt, "}}")
+            }
+        }
+    }
+}
+
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+pub struct VarDebugInfoFragment<'tcx> {
+    /// Where in the composite user variable this fragment is,
+    /// represented as a "projection" into the composite variable.
+    /// At lower levels, this corresponds to a byte/bit range.
+    // NOTE(eddyb) there's an unenforced invariant that this contains
+    // only `Field`s, and not into `enum` variants or `union`s.
+    // FIXME(eddyb) support this for `enum`s by either using DWARF's
+    // more advanced control-flow features (unsupported by LLVM?)
+    // to match on the discriminant, or by using custom type debuginfo
+    // with non-overlapping variants for the composite variable.
+    pub projection: Vec<PlaceElem<'tcx>>,
+
+    /// Where the data for this fragment can be found.
+    // NOTE(eddyb) There's an unenforced invariant that this `Place` is
+    // contains no indexing (with a non-constant index).
+    pub contents: Place<'tcx>,
+}
+
+impl Debug for VarDebugInfoFragment<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        for elem in self.projection.iter() {
+            match elem {
+                ProjectionElem::Field(field, _) => {
+                    write!(fmt, ".{:?}", field.index())?;
+                }
+                _ => bug!("unsupported fragment projection `{:?}`", elem),
+            }
         }
+
+        write!(fmt, " => {:?}", self.contents)
     }
 }
 
@@ -1541,7 +1594,7 @@ impl<'tcx> Place<'tcx> {
     /// If MirPhase >= Derefered and if projection contains Deref,
     /// It's guaranteed to be in the first place
     pub fn has_deref(&self) -> bool {
-        // To make sure this is not accidently used in wrong mir phase
+        // To make sure this is not accidentally used in wrong mir phase
         debug_assert!(
             self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
         );
@@ -2251,7 +2304,7 @@ impl<'tcx> ConstantKind<'tcx> {
                 // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
                 match tcx.const_eval_resolve(param_env, uneval, None) {
                     Ok(val) => Self::Val(val, ty),
-                    Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => self,
+                    Err(ErrorHandled::TooGeneric) => self,
                     Err(ErrorHandled::Reported(guar)) => {
                         Self::Ty(tcx.const_error_with_guaranteed(ty, guar))
                     }
@@ -2470,12 +2523,10 @@ impl<'tcx> ConstantKind<'tcx> {
             ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
                 // Find the name and index of the const parameter by indexing the generics of
                 // the parent item and construct a `ParamConst`.
-                let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
-                let item_id = tcx.hir().get_parent_node(hir_id);
-                let item_def_id = tcx.hir().local_def_id(item_id);
-                let generics = tcx.generics_of(item_def_id.to_def_id());
+                let item_def_id = tcx.parent(def_id);
+                let generics = tcx.generics_of(item_def_id);
                 let index = generics.param_def_id_to_index[&def_id];
-                let name = tcx.hir().name(hir_id);
+                let name = tcx.item_name(def_id);
                 let ty_const =
                     tcx.mk_const(ty::ConstKind::Param(ty::ParamConst::new(index, name)), ty);
                 debug!(?ty_const);
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 05dcfba77b2..2b3f2c02411 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -12,8 +12,8 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
 use rustc_index::vec::Idx;
 use rustc_middle::mir::interpret::{
-    read_target_uint, AllocId, Allocation, ConstAllocation, ConstValue, GlobalAlloc, Pointer,
-    Provenance,
+    alloc_range, read_target_uint, AllocId, Allocation, ConstAllocation, ConstValue, GlobalAlloc,
+    Pointer, Provenance,
 };
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::MirSource;
@@ -685,7 +685,7 @@ pub fn write_allocations<'tcx>(
     fn alloc_ids_from_alloc(
         alloc: ConstAllocation<'_>,
     ) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
-        alloc.inner().provenance().values().map(|id| *id)
+        alloc.inner().provenance().ptrs().values().map(|id| *id)
     }
 
     fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
@@ -788,7 +788,7 @@ pub fn write_allocations<'tcx>(
 /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
 /// characters or characters whose value is larger than 127) with a `.`
 /// This also prints provenance adequately.
-pub fn display_allocation<'a, 'tcx, Prov, Extra>(
+pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra>(
     tcx: TyCtxt<'tcx>,
     alloc: &'a Allocation<Prov, Extra>,
 ) -> RenderAllocation<'a, 'tcx, Prov, Extra> {
@@ -796,7 +796,7 @@ pub fn display_allocation<'a, 'tcx, Prov, Extra>(
 }
 
 #[doc(hidden)]
-pub struct RenderAllocation<'a, 'tcx, Prov, Extra> {
+pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra> {
     tcx: TyCtxt<'tcx>,
     alloc: &'a Allocation<Prov, Extra>,
 }
@@ -882,9 +882,9 @@ fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>(
         if i != line_start {
             write!(w, " ")?;
         }
-        if let Some(&prov) = alloc.provenance().get(&i) {
+        if let Some(prov) = alloc.provenance().get_ptr(i) {
             // Memory with provenance must be defined
-            assert!(alloc.init_mask().is_range_initialized(i, i + ptr_size).is_ok());
+            assert!(alloc.init_mask().is_range_initialized(alloc_range(i, ptr_size)).is_ok());
             let j = i.bytes_usize();
             let offset = alloc
                 .inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
@@ -904,9 +904,9 @@ fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>(
                 let overflow = ptr_size - remainder;
                 let remainder_width = provenance_width(remainder.bytes_usize()) - 2;
                 let overflow_width = provenance_width(overflow.bytes_usize() - 1) + 1;
-                ascii.push('╾');
-                for _ in 0..remainder.bytes() - 1 {
-                    ascii.push('─');
+                ascii.push('╾'); // HEAVY LEFT AND LIGHT RIGHT
+                for _ in 1..remainder.bytes() {
+                    ascii.push('─'); // LIGHT HORIZONTAL
                 }
                 if overflow_width > remainder_width && overflow_width >= target.len() {
                     // The case where the provenance fits into the part in the next line
@@ -926,7 +926,7 @@ fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>(
                 for _ in 0..overflow.bytes() - 1 {
                     ascii.push('─');
                 }
-                ascii.push('╼');
+                ascii.push('╼'); // LIGHT LEFT AND HEAVY RIGHT
                 i += ptr_size;
                 continue;
             } else {
@@ -941,7 +941,23 @@ fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>(
                 ascii.push('╼');
                 i += ptr_size;
             }
-        } else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() {
+        } else if let Some(prov) = alloc.provenance().get(i, &tcx) {
+            // Memory with provenance must be defined
+            assert!(
+                alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok()
+            );
+            ascii.push('━'); // HEAVY HORIZONTAL
+            // We have two characters to display this, which is obviously not enough.
+            // Format is similar to "oversized" above.
+            let j = i.bytes_usize();
+            let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
+            write!(w, "╾{:02x}{:#?} (1 ptr byte)╼", c, prov)?;
+            i += Size::from_bytes(1);
+        } else if alloc
+            .init_mask()
+            .is_range_initialized(alloc_range(i, Size::from_bytes(1)))
+            .is_ok()
+        {
             let j = i.bytes_usize();
 
             // Checked definedness (and thus range) and provenance. This access also doesn't
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 85ef51f129b..fed943169df 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -85,7 +85,7 @@ pub enum MirPhase {
     ///
     /// Also note that the lint pass which reports eg `200_u8 + 200_u8` as an error is run as a part
     /// of analysis to runtime MIR lowering. To ensure lints are reported reliably, this means that
-    /// transformations which may supress such errors should not run on analysis MIR.
+    /// transformations which may suppress such errors should not run on analysis MIR.
     Runtime(RuntimePhase),
 }
 
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index ddcf3711bfc..b21f50ae5ea 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -847,6 +847,17 @@ macro_rules! make_mir_visitor {
                             PlaceContext::NonUse(NonUseContext::VarDebugInfo),
                             location
                         ),
+                    VarDebugInfoContents::Composite { ty, fragments } => {
+                        // FIXME(eddyb) use a better `TyContext` here.
+                        self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+                        for VarDebugInfoFragment { projection: _, contents } in fragments {
+                            self.visit_place(
+                                contents,
+                                PlaceContext::NonUse(NonUseContext::VarDebugInfo),
+                                location,
+                            );
+                        }
+                    }
                 }
             }
 
@@ -1320,6 +1331,15 @@ impl PlaceContext {
         )
     }
 
+    /// Returns `true` if this place context represents an address-of.
+    pub fn is_address_of(&self) -> bool {
+        matches!(
+            self,
+            PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
+                | PlaceContext::MutatingUse(MutatingUseContext::AddressOf)
+        )
+    }
+
     /// Returns `true` if this place context represents a storage live or storage dead marker.
     #[inline]
     pub fn is_storage_marker(&self) -> bool {
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index b0a2412ab15..137b59cf6c2 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -463,9 +463,7 @@ impl<'tcx> AdtDef<'tcx> {
             }
             Err(err) => {
                 let msg = match err {
-                    ErrorHandled::Reported(_) | ErrorHandled::Linted => {
-                        "enum discriminant evaluation failed"
-                    }
+                    ErrorHandled::Reported(_) => "enum discriminant evaluation failed",
                     ErrorHandled::TooGeneric => "enum discriminant depends on generics",
                 };
                 tcx.sess.delay_span_bug(tcx.def_span(expr_did), msg);
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index e2e2761501b..37153a63944 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -1,6 +1,6 @@
 use crate::mir::interpret::LitToConstInput;
 use crate::mir::ConstantKind;
-use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
+use crate::ty::{self, DefIdTree, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
 use rustc_data_structures::intern::Interned;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -131,12 +131,10 @@ impl<'tcx> Const<'tcx> {
             ExprKind::Path(QPath::Resolved(_, &Path { res: Res::Def(ConstParam, def_id), .. })) => {
                 // Find the name and index of the const parameter by indexing the generics of
                 // the parent item and construct a `ParamConst`.
-                let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
-                let item_id = tcx.hir().get_parent_node(hir_id);
-                let item_def_id = tcx.hir().local_def_id(item_id);
-                let generics = tcx.generics_of(item_def_id.to_def_id());
+                let item_def_id = tcx.parent(def_id);
+                let generics = tcx.generics_of(item_def_id);
                 let index = generics.param_def_id_to_index[&def_id];
-                let name = tcx.hir().name(hir_id);
+                let name = tcx.item_name(def_id);
                 Some(tcx.mk_const(ty::ConstKind::Param(ty::ParamConst::new(index, name)), ty))
             }
             _ => None,
@@ -268,9 +266,9 @@ impl<'tcx> Const<'tcx> {
 pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Const<'tcx> {
     let default_def_id = match tcx.hir().get_by_def_id(def_id.expect_local()) {
         hir::Node::GenericParam(hir::GenericParam {
-            kind: hir::GenericParamKind::Const { ty: _, default: Some(ac) },
+            kind: hir::GenericParamKind::Const { default: Some(ac), .. },
             ..
-        }) => tcx.hir().local_def_id(ac.hir_id),
+        }) => ac.def_id,
         _ => span_bug!(
             tcx.def_span(def_id),
             "`const_param_default` expected a generic parameter with a constant"
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index c1c613f6c60..321cba693d9 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -226,7 +226,7 @@ impl<'tcx> ConstKind<'tcx> {
                         // (which may be identity substs, see above),
                         // can leak through `val` into the const we return.
                         Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
-                        Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None,
+                        Err(ErrorHandled::TooGeneric) => None,
                         Err(ErrorHandled::Reported(e)) => Some(Err(e)),
                     }
                 }
@@ -237,7 +237,7 @@ impl<'tcx> ConstKind<'tcx> {
                         // (which may be identity substs, see above),
                         // can leak through `val` into the const we return.
                         Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
-                        Err(ErrorHandled::TooGeneric | ErrorHandled::Linted) => None,
+                        Err(ErrorHandled::TooGeneric) => None,
                         Err(ErrorHandled::Reported(e)) => Some(Err(e)),
                     }
                 }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 8f96f5a9eb3..1c714f59425 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -41,7 +41,7 @@ use rustc_errors::{
 };
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, LOCAL_CRATE};
 use rustc_hir::definitions::Definitions;
 use rustc_hir::hir_id::OwnerId;
 use rustc_hir::intravisit::Visitor;
@@ -443,7 +443,7 @@ pub struct TypeckResults<'tcx> {
 
     /// Stores the canonicalized types provided by the user. See also
     /// `AscribeUserType` statement in MIR.
-    pub user_provided_sigs: DefIdMap<CanonicalPolyFnSig<'tcx>>,
+    pub user_provided_sigs: LocalDefIdMap<CanonicalPolyFnSig<'tcx>>,
 
     adjustments: ItemLocalMap<Vec<ty::adjustment::Adjustment<'tcx>>>,
 
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index b8fd01e6a77..029ee15d68d 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -151,7 +151,6 @@ enum SuggestChangingConstraintsMessage<'a> {
 }
 
 fn suggest_removing_unsized_bound(
-    tcx: TyCtxt<'_>,
     generics: &hir::Generics<'_>,
     suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
     param: &hir::GenericParam<'_>,
@@ -160,17 +159,16 @@ fn suggest_removing_unsized_bound(
     // See if there's a `?Sized` bound that can be removed to suggest that.
     // First look at the `where` clause because we can have `where T: ?Sized`,
     // then look at params.
-    let param_def_id = tcx.hir().local_def_id(param.hir_id);
     for (where_pos, predicate) in generics.predicates.iter().enumerate() {
         let WherePredicate::BoundPredicate(predicate) = predicate else {
             continue;
         };
-        if !predicate.is_param_bound(param_def_id.to_def_id()) {
+        if !predicate.is_param_bound(param.def_id.to_def_id()) {
             continue;
         };
 
         for (pos, bound) in predicate.bounds.iter().enumerate() {
-            let    hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
+            let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
                 continue;
             };
             if poly.trait_ref.trait_def_id() != def_id {
@@ -232,7 +230,7 @@ pub fn suggest_constraining_type_params<'a>(
                     param.span,
                     &format!("this type parameter needs to be `{}`", constraint),
                 );
-                suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id);
+                suggest_removing_unsized_bound(generics, &mut suggestions, param, def_id);
             }
         }
 
@@ -283,8 +281,7 @@ pub fn suggest_constraining_type_params<'a>(
         //          --
         //          |
         //          replace with: `T: Bar +`
-        let param_def_id = tcx.hir().local_def_id(param.hir_id);
-        if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) {
+        if let Some(span) = generics.bounds_span_for_suggestions(param.def_id) {
             suggest_restrict(span, true);
             continue;
         }
diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs
index d1c0d62ac6e..3e59c0b967c 100644
--- a/compiler/rustc_middle/src/ty/impls_ty.rs
+++ b/compiler/rustc_middle/src/ty/impls_ty.rs
@@ -112,19 +112,6 @@ impl<'a> HashStable<StableHashingContext<'a>> for mir::interpret::AllocId {
     }
 }
 
-// `Relocations` with default type parameters is a sorted map.
-impl<'a, Prov> HashStable<StableHashingContext<'a>> for mir::interpret::ProvenanceMap<Prov>
-where
-    Prov: HashStable<StableHashingContext<'a>>,
-{
-    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
-        self.len().hash_stable(hcx, hasher);
-        for reloc in self.iter() {
-            reloc.hash_stable(hcx, hasher);
-        }
-    }
-}
-
 impl<'a> ToStableHashKey<StableHashingContext<'a>> for region::Scope {
     type KeyType = region::Scope;
 
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index 2bcb2d82484..c1cf7896db5 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -6,7 +6,6 @@ use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts};
 use crate::ty::visit::{TypeVisitable, TypeVisitor};
 use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
 
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::intern::{Interned, WithStableHash};
 use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
@@ -19,7 +18,7 @@ use std::fmt;
 use std::marker::PhantomData;
 use std::mem;
 use std::num::NonZeroUsize;
-use std::ops::ControlFlow;
+use std::ops::{ControlFlow, Deref};
 use std::slice;
 
 /// An entity in the Rust type system, which can be one of
@@ -562,25 +561,86 @@ impl<T, U> EarlyBinder<(T, U)> {
     }
 }
 
-impl<'tcx, 's, T: IntoIterator<Item = I>, I: TypeFoldable<'tcx>> EarlyBinder<T> {
+impl<'tcx, 's, I: IntoIterator> EarlyBinder<I>
+where
+    I::Item: TypeFoldable<'tcx>,
+{
     pub fn subst_iter(
         self,
         tcx: TyCtxt<'tcx>,
         substs: &'s [GenericArg<'tcx>],
-    ) -> impl Iterator<Item = I> + Captures<'s> + Captures<'tcx> {
-        self.0.into_iter().map(move |t| EarlyBinder(t).subst(tcx, substs))
+    ) -> SubstIter<'s, 'tcx, I> {
+        SubstIter { it: self.0.into_iter(), tcx, substs }
+    }
+}
+
+pub struct SubstIter<'s, 'tcx, I: IntoIterator> {
+    it: I::IntoIter,
+    tcx: TyCtxt<'tcx>,
+    substs: &'s [GenericArg<'tcx>],
+}
+
+impl<'tcx, I: IntoIterator> Iterator for SubstIter<'_, 'tcx, I>
+where
+    I::Item: TypeFoldable<'tcx>,
+{
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        Some(EarlyBinder(self.it.next()?).subst(self.tcx, self.substs))
+    }
+}
+
+impl<'tcx, I: IntoIterator> DoubleEndedIterator for SubstIter<'_, 'tcx, I>
+where
+    I::IntoIter: DoubleEndedIterator,
+    I::Item: TypeFoldable<'tcx>,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        Some(EarlyBinder(self.it.next_back()?).subst(self.tcx, self.substs))
     }
 }
 
-impl<'tcx, 's, 'a, T: IntoIterator<Item = &'a I>, I: Copy + TypeFoldable<'tcx> + 'a>
-    EarlyBinder<T>
+impl<'tcx, 's, I: IntoIterator> EarlyBinder<I>
+where
+    I::Item: Deref,
+    <I::Item as Deref>::Target: Copy + TypeFoldable<'tcx>,
 {
     pub fn subst_iter_copied(
         self,
         tcx: TyCtxt<'tcx>,
         substs: &'s [GenericArg<'tcx>],
-    ) -> impl Iterator<Item = I> + Captures<'s> + Captures<'tcx> + Captures<'a> {
-        self.0.into_iter().map(move |t| EarlyBinder(*t).subst(tcx, substs))
+    ) -> SubstIterCopied<'s, 'tcx, I> {
+        SubstIterCopied { it: self.0.into_iter(), tcx, substs }
+    }
+}
+
+pub struct SubstIterCopied<'a, 'tcx, I: IntoIterator> {
+    it: I::IntoIter,
+    tcx: TyCtxt<'tcx>,
+    substs: &'a [GenericArg<'tcx>],
+}
+
+impl<'tcx, I: IntoIterator> Iterator for SubstIterCopied<'_, 'tcx, I>
+where
+    I::Item: Deref,
+    <I::Item as Deref>::Target: Copy + TypeFoldable<'tcx>,
+{
+    type Item = <I::Item as Deref>::Target;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        Some(EarlyBinder(*self.it.next()?).subst(self.tcx, self.substs))
+    }
+}
+
+impl<'tcx, I: IntoIterator> DoubleEndedIterator for SubstIterCopied<'_, 'tcx, I>
+where
+    I::IntoIter: DoubleEndedIterator,
+    I::Item: Deref,
+    <I::Item as Deref>::Target: Copy + TypeFoldable<'tcx>,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        Some(EarlyBinder(*self.it.next_back()?).subst(self.tcx, self.substs))
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs
index 5ca51c25a9c..6eae94511e4 100644
--- a/compiler/rustc_middle/src/ty/vtable.rs
+++ b/compiler/rustc_middle/src/ty/vtable.rs
@@ -66,7 +66,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
     let layout = tcx
         .layout_of(ty::ParamEnv::reveal_all().and(ty))
         .expect("failed to build vtable representation");
-    assert!(!layout.is_unsized(), "can't create a vtable for an unsized type");
+    assert!(layout.is_sized(), "can't create a vtable for an unsized type");
     let size = layout.size.bytes();
     let align = layout.align.abi.bytes();
 
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
index 183db56d7a0..db05592ed0e 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -118,7 +118,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     else_block: Some(else_block),
                 } => {
                     // When lowering the statement `let <pat> = <expr> else { <else> };`,
-                    // the `<else>` block is nested in the parent scope enclosing this statment.
+                    // the `<else>` block is nested in the parent scope enclosing this statement.
                     // That scope is usually either the enclosing block scope,
                     // or the remainder scope of the last statement.
                     // This is to make sure that temporaries instantiated in `<expr>` are dropped
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index c4639d3a513..57382f5e1bd 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -608,24 +608,22 @@ impl<'tcx> Cx<'tcx> {
                             out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)),
                         },
                         hir::InlineAsmOperand::Const { ref anon_const } => {
-                            let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id);
                             let value = mir::ConstantKind::from_anon_const(
                                 tcx,
-                                anon_const_def_id,
+                                anon_const.def_id,
                                 self.param_env,
                             );
-                            let span = tcx.hir().span(anon_const.hir_id);
+                            let span = tcx.def_span(anon_const.def_id);
 
                             InlineAsmOperand::Const { value, span }
                         }
                         hir::InlineAsmOperand::SymFn { ref anon_const } => {
-                            let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id);
                             let value = mir::ConstantKind::from_anon_const(
                                 tcx,
-                                anon_const_def_id,
+                                anon_const.def_id,
                                 self.param_env,
                             );
-                            let span = tcx.hir().span(anon_const.hir_id);
+                            let span = tcx.def_span(anon_const.def_id);
 
                             InlineAsmOperand::SymFn { value, span }
                         }
@@ -640,7 +638,7 @@ impl<'tcx> Cx<'tcx> {
 
             hir::ExprKind::ConstBlock(ref anon_const) => {
                 let ty = self.typeck_results().node_type(anon_const.hir_id);
-                let did = tcx.hir().local_def_id(anon_const.hir_id).to_def_id();
+                let did = anon_const.def_id.to_def_id();
                 let typeck_root_def_id = tcx.typeck_root_def_id(did);
                 let parent_substs =
                     tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
@@ -859,9 +857,7 @@ impl<'tcx> Cx<'tcx> {
 
             Res::Def(DefKind::ConstParam, def_id) => {
                 let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
-                let item_id = self.tcx.hir().get_parent_node(hir_id);
-                let item_def_id = self.tcx.hir().local_def_id(item_id);
-                let generics = self.tcx.generics_of(item_def_id);
+                let generics = self.tcx.generics_of(hir_id.owner);
                 let index = generics.param_def_id_to_index[&def_id];
                 let name = self.tcx.hir().name(hir_id);
                 let param = ty::ParamConst::new(index, name);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 776c748c7e5..4b6608faba6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -565,8 +565,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         id: hir::HirId,
         span: Span,
     ) -> PatKind<'tcx> {
-        let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
-        let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const_def_id);
+        let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const.def_id);
 
         // Evaluate early like we do in `lower_path`.
         let value = value.eval(self.tcx, self.param_env);
@@ -577,6 +576,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     self.errors.push(PatternError::ConstParamInPattern(span));
                     return PatKind::Wild;
                 }
+                ConstKind::Error(_) => {
+                    return PatKind::Wild;
+                }
                 _ => bug!("Expected ConstKind::Param"),
             },
             mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind,
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index 579fe68a149..c9d5601f207 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -475,7 +475,10 @@ where
                 r#"<td colspan="{colspan}" {fmt} align="left">{state}</td>"#,
                 colspan = this.style.num_state_columns(),
                 fmt = fmt,
-                state = format!("{:?}", DebugWithAdapter { this: state, ctxt: analysis }),
+                state = dot::escape_html(&format!(
+                    "{:?}",
+                    DebugWithAdapter { this: state, ctxt: analysis }
+                )),
             )
         })
     }
diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
index d6b89eb8227..f0e75c53ea1 100644
--- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
@@ -73,6 +73,16 @@ pub trait MeetSemiLattice: Eq {
     fn meet(&mut self, other: &Self) -> bool;
 }
 
+/// A set that has a "bottom" element, which is less than or equal to any other element.
+pub trait HasBottom {
+    fn bottom() -> Self;
+}
+
+/// A set that has a "top" element, which is greater than or equal to any other element.
+pub trait HasTop {
+    fn top() -> Self;
+}
+
 /// A `bool` is a "two-point" lattice with `true` as the top element and `false` as the bottom:
 ///
 /// ```text
@@ -102,6 +112,18 @@ impl MeetSemiLattice for bool {
     }
 }
 
+impl HasBottom for bool {
+    fn bottom() -> Self {
+        false
+    }
+}
+
+impl HasTop for bool {
+    fn top() -> Self {
+        true
+    }
+}
+
 /// A tuple (or list) of lattices is itself a lattice whose least upper bound is the concatenation
 /// of the least upper bounds of each element of the tuple (or list).
 ///
@@ -250,3 +272,15 @@ impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
         true
     }
 }
+
+impl<T> HasBottom for FlatSet<T> {
+    fn bottom() -> Self {
+        Self::Bottom
+    }
+}
+
+impl<T> HasTop for FlatSet<T> {
+    fn top() -> Self {
+        Self::Top
+    }
+}
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index b471d04fd60..7f40cfca32f 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -41,6 +41,7 @@ pub mod move_paths;
 pub mod rustc_peek;
 pub mod storage;
 pub mod un_derefer;
+pub mod value_analysis;
 
 pub(crate) mod indexes {
     pub(crate) use super::move_paths::MovePathIndex;
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
new file mode 100644
index 00000000000..db4b0a3deda
--- /dev/null
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -0,0 +1,927 @@
+//! This module provides a framework on top of the normal MIR dataflow framework to simplify the
+//! implementation of analyses that track information about the values stored in certain places.
+//! We are using the term "place" here to refer to a `mir::Place` (a place expression) instead of
+//! an `interpret::Place` (a memory location).
+//!
+//! The default methods of [`ValueAnalysis`] (prefixed with `super_` instead of `handle_`)
+//! provide some behavior that should be valid for all abstract domains that are based only on the
+//! value stored in a certain place. On top of these default rules, an implementation should
+//! override some of the `handle_` methods. For an example, see `ConstAnalysis`.
+//!
+//! An implementation must also provide a [`Map`]. Before the analysis begins, all places that
+//! should be tracked during the analysis must be registered. During the analysis, no new places
+//! can be registered. The [`State`] can be queried to retrieve the abstract value stored for a
+//! certain place by passing the map.
+//!
+//! This framework is currently experimental. Originally, it supported shared references and enum
+//! variants. However, it was discovered that both of these were unsound, and especially references
+//! had subtle but serious issues. In the future, they could be added back in, but we should clarify
+//! the rules for optimizations that rely on the aliasing model first.
+//!
+//!
+//! # Notes
+//!
+//! - The bottom state denotes uninitialized memory. Because we are only doing a sound approximation
+//! of the actual execution, we can also use this state for places where access would be UB.
+//!
+//! - The assignment logic in `State::assign_place_idx` assumes that the places are non-overlapping,
+//! or identical. Note that this refers to place expressions, not memory locations.
+//!
+//! - Currently, places that have their reference taken cannot be tracked. Although this would be
+//! possible, it has to rely on some aliasing model, which we are not ready to commit to yet.
+//! Because of that, we can assume that the only way to change the value behind a tracked place is
+//! by direct assignment.
+
+use std::fmt::{Debug, Formatter};
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::*;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_target::abi::VariantIdx;
+
+use crate::lattice::{HasBottom, HasTop};
+use crate::{
+    fmt::DebugWithContext, Analysis, AnalysisDomain, CallReturnPlaces, JoinSemiLattice,
+    SwitchIntEdgeEffects,
+};
+
+pub trait ValueAnalysis<'tcx> {
+    /// For each place of interest, the analysis tracks a value of the given type.
+    type Value: Clone + JoinSemiLattice + HasBottom + HasTop;
+
+    const NAME: &'static str;
+
+    fn map(&self) -> &Map;
+
+    fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
+        self.super_statement(statement, state)
+    }
+
+    fn super_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
+        match &statement.kind {
+            StatementKind::Assign(box (place, rvalue)) => {
+                self.handle_assign(*place, rvalue, state);
+            }
+            StatementKind::SetDiscriminant { .. } => {
+                // Could treat this as writing a constant to a pseudo-place.
+                // But discriminants are currently not tracked, so we do nothing.
+                // Related: https://github.com/rust-lang/unsafe-code-guidelines/issues/84
+            }
+            StatementKind::Intrinsic(box intrinsic) => {
+                self.handle_intrinsic(intrinsic, state);
+            }
+            StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
+                // StorageLive leaves the local in an uninitialized state.
+                // StorageDead makes it UB to access the local afterwards.
+                state.flood_with(Place::from(*local).as_ref(), self.map(), Self::Value::bottom());
+            }
+            StatementKind::Deinit(box place) => {
+                // Deinit makes the place uninitialized.
+                state.flood_with(place.as_ref(), self.map(), Self::Value::bottom());
+            }
+            StatementKind::Retag(..) => {
+                // We don't track references.
+            }
+            StatementKind::Nop
+            | StatementKind::FakeRead(..)
+            | StatementKind::Coverage(..)
+            | StatementKind::AscribeUserType(..) => (),
+        }
+    }
+
+    fn handle_intrinsic(
+        &self,
+        intrinsic: &NonDivergingIntrinsic<'tcx>,
+        state: &mut State<Self::Value>,
+    ) {
+        self.super_intrinsic(intrinsic, state);
+    }
+
+    fn super_intrinsic(
+        &self,
+        intrinsic: &NonDivergingIntrinsic<'tcx>,
+        state: &mut State<Self::Value>,
+    ) {
+        match intrinsic {
+            NonDivergingIntrinsic::Assume(..) => {
+                // Could use this, but ignoring it is sound.
+            }
+            NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { dst, .. }) => {
+                if let Some(place) = dst.place() {
+                    state.flood(place.as_ref(), self.map());
+                }
+            }
+        }
+    }
+
+    fn handle_assign(
+        &self,
+        target: Place<'tcx>,
+        rvalue: &Rvalue<'tcx>,
+        state: &mut State<Self::Value>,
+    ) {
+        self.super_assign(target, rvalue, state)
+    }
+
+    fn super_assign(
+        &self,
+        target: Place<'tcx>,
+        rvalue: &Rvalue<'tcx>,
+        state: &mut State<Self::Value>,
+    ) {
+        let result = self.handle_rvalue(rvalue, state);
+        state.assign(target.as_ref(), result, self.map());
+    }
+
+    fn handle_rvalue(
+        &self,
+        rvalue: &Rvalue<'tcx>,
+        state: &mut State<Self::Value>,
+    ) -> ValueOrPlace<Self::Value> {
+        self.super_rvalue(rvalue, state)
+    }
+
+    fn super_rvalue(
+        &self,
+        rvalue: &Rvalue<'tcx>,
+        state: &mut State<Self::Value>,
+    ) -> ValueOrPlace<Self::Value> {
+        match rvalue {
+            Rvalue::Use(operand) => self.handle_operand(operand, state),
+            Rvalue::CopyForDeref(place) => self.handle_operand(&Operand::Copy(*place), state),
+            Rvalue::Ref(..) | Rvalue::AddressOf(..) => {
+                // We don't track such places.
+                ValueOrPlace::top()
+            }
+            Rvalue::Repeat(..)
+            | Rvalue::ThreadLocalRef(..)
+            | Rvalue::Len(..)
+            | Rvalue::Cast(..)
+            | Rvalue::BinaryOp(..)
+            | Rvalue::CheckedBinaryOp(..)
+            | Rvalue::NullaryOp(..)
+            | Rvalue::UnaryOp(..)
+            | Rvalue::Discriminant(..)
+            | Rvalue::Aggregate(..)
+            | Rvalue::ShallowInitBox(..) => {
+                // No modification is possible through these r-values.
+                ValueOrPlace::top()
+            }
+        }
+    }
+
+    fn handle_operand(
+        &self,
+        operand: &Operand<'tcx>,
+        state: &mut State<Self::Value>,
+    ) -> ValueOrPlace<Self::Value> {
+        self.super_operand(operand, state)
+    }
+
+    fn super_operand(
+        &self,
+        operand: &Operand<'tcx>,
+        state: &mut State<Self::Value>,
+    ) -> ValueOrPlace<Self::Value> {
+        match operand {
+            Operand::Constant(box constant) => {
+                ValueOrPlace::Value(self.handle_constant(constant, state))
+            }
+            Operand::Copy(place) | Operand::Move(place) => {
+                // On move, we would ideally flood the place with bottom. But with the current
+                // framework this is not possible (similar to `InterpCx::eval_operand`).
+                self.map()
+                    .find(place.as_ref())
+                    .map(ValueOrPlace::Place)
+                    .unwrap_or(ValueOrPlace::top())
+            }
+        }
+    }
+
+    fn handle_constant(
+        &self,
+        constant: &Constant<'tcx>,
+        state: &mut State<Self::Value>,
+    ) -> Self::Value {
+        self.super_constant(constant, state)
+    }
+
+    fn super_constant(
+        &self,
+        _constant: &Constant<'tcx>,
+        _state: &mut State<Self::Value>,
+    ) -> Self::Value {
+        Self::Value::top()
+    }
+
+    /// The effect of a successful function call return should not be
+    /// applied here, see [`Analysis::apply_terminator_effect`].
+    fn handle_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>) {
+        self.super_terminator(terminator, state)
+    }
+
+    fn super_terminator(&self, terminator: &Terminator<'tcx>, _state: &mut State<Self::Value>) {
+        match &terminator.kind {
+            TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => {
+                // Effect is applied by `handle_call_return`.
+            }
+            TerminatorKind::Drop { .. } => {
+                // We don't track dropped places.
+            }
+            TerminatorKind::DropAndReplace { .. } | TerminatorKind::Yield { .. } => {
+                // They would have an effect, but are not allowed in this phase.
+                bug!("encountered disallowed terminator");
+            }
+            TerminatorKind::Goto { .. }
+            | TerminatorKind::SwitchInt { .. }
+            | TerminatorKind::Resume
+            | TerminatorKind::Abort
+            | TerminatorKind::Return
+            | TerminatorKind::Unreachable
+            | TerminatorKind::Assert { .. }
+            | TerminatorKind::GeneratorDrop
+            | TerminatorKind::FalseEdge { .. }
+            | TerminatorKind::FalseUnwind { .. } => {
+                // These terminators have no effect on the analysis.
+            }
+        }
+    }
+
+    fn handle_call_return(
+        &self,
+        return_places: CallReturnPlaces<'_, 'tcx>,
+        state: &mut State<Self::Value>,
+    ) {
+        self.super_call_return(return_places, state)
+    }
+
+    fn super_call_return(
+        &self,
+        return_places: CallReturnPlaces<'_, 'tcx>,
+        state: &mut State<Self::Value>,
+    ) {
+        return_places.for_each(|place| {
+            state.flood(place.as_ref(), self.map());
+        })
+    }
+
+    fn handle_switch_int(
+        &self,
+        discr: &Operand<'tcx>,
+        apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
+    ) {
+        self.super_switch_int(discr, apply_edge_effects)
+    }
+
+    fn super_switch_int(
+        &self,
+        _discr: &Operand<'tcx>,
+        _apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
+    ) {
+    }
+
+    fn wrap(self) -> ValueAnalysisWrapper<Self>
+    where
+        Self: Sized,
+    {
+        ValueAnalysisWrapper(self)
+    }
+}
+
+pub struct ValueAnalysisWrapper<T>(pub T);
+
+impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper<T> {
+    type Domain = State<T::Value>;
+
+    type Direction = crate::Forward;
+
+    const NAME: &'static str = T::NAME;
+
+    fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
+        State(StateData::Unreachable)
+    }
+
+    fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
+        // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥.
+        assert!(matches!(state.0, StateData::Unreachable));
+        let values = IndexVec::from_elem_n(T::Value::bottom(), self.0.map().value_count);
+        *state = State(StateData::Reachable(values));
+        for arg in body.args_iter() {
+            state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map());
+        }
+    }
+}
+
+impl<'tcx, T> Analysis<'tcx> for ValueAnalysisWrapper<T>
+where
+    T: ValueAnalysis<'tcx>,
+{
+    fn apply_statement_effect(
+        &self,
+        state: &mut Self::Domain,
+        statement: &Statement<'tcx>,
+        _location: Location,
+    ) {
+        if state.is_reachable() {
+            self.0.handle_statement(statement, state);
+        }
+    }
+
+    fn apply_terminator_effect(
+        &self,
+        state: &mut Self::Domain,
+        terminator: &Terminator<'tcx>,
+        _location: Location,
+    ) {
+        if state.is_reachable() {
+            self.0.handle_terminator(terminator, state);
+        }
+    }
+
+    fn apply_call_return_effect(
+        &self,
+        state: &mut Self::Domain,
+        _block: BasicBlock,
+        return_places: crate::CallReturnPlaces<'_, 'tcx>,
+    ) {
+        if state.is_reachable() {
+            self.0.handle_call_return(return_places, state)
+        }
+    }
+
+    fn apply_switch_int_edge_effects(
+        &self,
+        _block: BasicBlock,
+        discr: &Operand<'tcx>,
+        apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
+    ) {
+        // FIXME: Dataflow framework provides no access to current state here.
+        self.0.handle_switch_int(discr, apply_edge_effects)
+    }
+}
+
+rustc_index::newtype_index!(
+    /// This index uniquely identifies a place.
+    ///
+    /// Not every place has a `PlaceIndex`, and not every `PlaceIndex` correspondends to a tracked
+    /// place. However, every tracked place and all places along its projection have a `PlaceIndex`.
+    pub struct PlaceIndex {}
+);
+
+rustc_index::newtype_index!(
+    /// This index uniquely identifies a tracked place and therefore a slot in [`State`].
+    ///
+    /// It is an implementation detail of this module.
+    struct ValueIndex {}
+);
+
+/// See [`State`].
+#[derive(PartialEq, Eq, Debug)]
+enum StateData<V> {
+    Reachable(IndexVec<ValueIndex, V>),
+    Unreachable,
+}
+
+impl<V: Clone> Clone for StateData<V> {
+    fn clone(&self) -> Self {
+        match self {
+            Self::Reachable(x) => Self::Reachable(x.clone()),
+            Self::Unreachable => Self::Unreachable,
+        }
+    }
+
+    fn clone_from(&mut self, source: &Self) {
+        match (&mut *self, source) {
+            (Self::Reachable(x), Self::Reachable(y)) => {
+                // We go through `raw` here, because `IndexVec` currently has a naive `clone_from`.
+                x.raw.clone_from(&y.raw);
+            }
+            _ => *self = source.clone(),
+        }
+    }
+}
+
+/// The dataflow state for an instance of [`ValueAnalysis`].
+///
+/// Every instance specifies a lattice that represents the possible values of a single tracked
+/// place. If we call this lattice `V` and set set of tracked places `P`, then a [`State`] is an
+/// element of `{unreachable} ∪ (P -> V)`. This again forms a lattice, where the bottom element is
+/// `unreachable` and the top element is the mapping `p ↦ ⊤`. Note that the mapping `p ↦ ⊥` is not
+/// the bottom element (because joining an unreachable and any other reachable state yields a
+/// reachable state). All operations on unreachable states are ignored.
+///
+/// Flooding means assigning a value (by default `⊤`) to all tracked projections of a given place.
+#[derive(PartialEq, Eq, Debug)]
+pub struct State<V>(StateData<V>);
+
+impl<V: Clone> Clone for State<V> {
+    fn clone(&self) -> Self {
+        Self(self.0.clone())
+    }
+
+    fn clone_from(&mut self, source: &Self) {
+        self.0.clone_from(&source.0);
+    }
+}
+
+impl<V: Clone + HasTop + HasBottom> State<V> {
+    pub fn is_reachable(&self) -> bool {
+        matches!(&self.0, StateData::Reachable(_))
+    }
+
+    pub fn mark_unreachable(&mut self) {
+        self.0 = StateData::Unreachable;
+    }
+
+    pub fn flood_all(&mut self) {
+        self.flood_all_with(V::top())
+    }
+
+    pub fn flood_all_with(&mut self, value: V) {
+        let StateData::Reachable(values) = &mut self.0 else { return };
+        values.raw.fill(value);
+    }
+
+    pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
+        if let Some(root) = map.find(place) {
+            self.flood_idx_with(root, map, value);
+        }
+    }
+
+    pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) {
+        self.flood_with(place, map, V::top())
+    }
+
+    pub fn flood_idx_with(&mut self, place: PlaceIndex, map: &Map, value: V) {
+        let StateData::Reachable(values) = &mut self.0 else { return };
+        map.preorder_invoke(place, &mut |place| {
+            if let Some(vi) = map.places[place].value_index {
+                values[vi] = value.clone();
+            }
+        });
+    }
+
+    pub fn flood_idx(&mut self, place: PlaceIndex, map: &Map) {
+        self.flood_idx_with(place, map, V::top())
+    }
+
+    /// Copies `source` to `target`, including all tracked places beneath.
+    ///
+    /// If `target` contains a place that is not contained in `source`, it will be overwritten with
+    /// Top. Also, because this will copy all entries one after another, it may only be used for
+    /// places that are non-overlapping or identical.
+    pub fn assign_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
+        let StateData::Reachable(values) = &mut self.0 else { return };
+
+        // If both places are tracked, we copy the value to the target. If the target is tracked,
+        // but the source is not, we have to invalidate the value in target. If the target is not
+        // tracked, then we don't have to do anything.
+        if let Some(target_value) = map.places[target].value_index {
+            if let Some(source_value) = map.places[source].value_index {
+                values[target_value] = values[source_value].clone();
+            } else {
+                values[target_value] = V::top();
+            }
+        }
+        for target_child in map.children(target) {
+            // Try to find corresponding child and recurse. Reasoning is similar as above.
+            let projection = map.places[target_child].proj_elem.unwrap();
+            if let Some(source_child) = map.projections.get(&(source, projection)) {
+                self.assign_place_idx(target_child, *source_child, map);
+            } else {
+                self.flood_idx(target_child, map);
+            }
+        }
+    }
+
+    pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) {
+        if let Some(target) = map.find(target) {
+            self.assign_idx(target, result, map);
+        } else {
+            // We don't track this place nor any projections, assignment can be ignored.
+        }
+    }
+
+    pub fn assign_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map) {
+        match result {
+            ValueOrPlace::Value(value) => {
+                // First flood the target place in case we also track any projections (although
+                // this scenario is currently not well-supported by the API).
+                self.flood_idx(target, map);
+                let StateData::Reachable(values) = &mut self.0 else { return };
+                if let Some(value_index) = map.places[target].value_index {
+                    values[value_index] = value;
+                }
+            }
+            ValueOrPlace::Place(source) => self.assign_place_idx(target, source, map),
+        }
+    }
+
+    /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
+    pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V {
+        map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::top())
+    }
+
+    /// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
+    pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V {
+        match &self.0 {
+            StateData::Reachable(values) => {
+                map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::top())
+            }
+            StateData::Unreachable => {
+                // Because this is unreachable, we can return any value we want.
+                V::bottom()
+            }
+        }
+    }
+}
+
+impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> {
+    fn join(&mut self, other: &Self) -> bool {
+        match (&mut self.0, &other.0) {
+            (_, StateData::Unreachable) => false,
+            (StateData::Unreachable, _) => {
+                *self = other.clone();
+                true
+            }
+            (StateData::Reachable(this), StateData::Reachable(other)) => this.join(other),
+        }
+    }
+}
+
+/// Partial mapping from [`Place`] to [`PlaceIndex`], where some places also have a [`ValueIndex`].
+///
+/// This data structure essentially maintains a tree of places and their projections. Some
+/// additional bookkeeping is done, to speed up traversal over this tree:
+/// - For iteration, every [`PlaceInfo`] contains an intrusive linked list of its children.
+/// - To directly get the child for a specific projection, there is a `projections` map.
+#[derive(Debug)]
+pub struct Map {
+    locals: IndexVec<Local, Option<PlaceIndex>>,
+    projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
+    places: IndexVec<PlaceIndex, PlaceInfo>,
+    value_count: usize,
+}
+
+impl Map {
+    fn new() -> Self {
+        Self {
+            locals: IndexVec::new(),
+            projections: FxHashMap::default(),
+            places: IndexVec::new(),
+            value_count: 0,
+        }
+    }
+
+    /// Returns a map that only tracks places whose type passes the filter.
+    ///
+    /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
+    /// chosen is an implementation detail and may not be relied upon (other than that their type
+    /// passes the filter).
+    #[instrument(skip_all, level = "debug")]
+    pub fn from_filter<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        body: &Body<'tcx>,
+        filter: impl FnMut(Ty<'tcx>) -> bool,
+    ) -> Self {
+        let mut map = Self::new();
+        let exclude = excluded_locals(body);
+        map.register_with_filter(tcx, body, filter, &exclude);
+        debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len());
+        map
+    }
+
+    /// Register all non-excluded places that pass the filter.
+    fn register_with_filter<'tcx>(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        body: &Body<'tcx>,
+        mut filter: impl FnMut(Ty<'tcx>) -> bool,
+        exclude: &IndexVec<Local, bool>,
+    ) {
+        // We use this vector as stack, pushing and popping projections.
+        let mut projection = Vec::new();
+        for (local, decl) in body.local_decls.iter_enumerated() {
+            if !exclude[local] {
+                self.register_with_filter_rec(tcx, local, &mut projection, decl.ty, &mut filter);
+            }
+        }
+    }
+
+    /// Potentially register the (local, projection) place and its fields, recursively.
+    ///
+    /// Invariant: The projection must only contain fields.
+    fn register_with_filter_rec<'tcx>(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        local: Local,
+        projection: &mut Vec<PlaceElem<'tcx>>,
+        ty: Ty<'tcx>,
+        filter: &mut impl FnMut(Ty<'tcx>) -> bool,
+    ) {
+        // Note: The framework supports only scalars for now.
+        if filter(ty) && ty.is_scalar() {
+            // We know that the projection only contains trackable elements.
+            let place = self.make_place(local, projection).unwrap();
+
+            // Allocate a value slot if it doesn't have one.
+            if self.places[place].value_index.is_none() {
+                self.places[place].value_index = Some(self.value_count.into());
+                self.value_count += 1;
+            }
+        }
+
+        // Recurse with all fields of this place.
+        iter_fields(ty, tcx, |variant, field, ty| {
+            if variant.is_some() {
+                // Downcasts are currently not supported.
+                return;
+            }
+            projection.push(PlaceElem::Field(field, ty));
+            self.register_with_filter_rec(tcx, local, projection, ty, filter);
+            projection.pop();
+        });
+    }
+
+    /// Tries to add the place to the map, without allocating a value slot.
+    ///
+    /// Can fail if the projection contains non-trackable elements.
+    fn make_place<'tcx>(
+        &mut self,
+        local: Local,
+        projection: &[PlaceElem<'tcx>],
+    ) -> Result<PlaceIndex, ()> {
+        // Get the base index of the local.
+        let mut index =
+            *self.locals.get_or_insert_with(local, || self.places.push(PlaceInfo::new(None)));
+
+        // Apply the projection.
+        for &elem in projection {
+            let elem = elem.try_into()?;
+            index = *self.projections.entry((index, elem)).or_insert_with(|| {
+                // Prepend new child to the linked list.
+                let next = self.places.push(PlaceInfo::new(Some(elem)));
+                self.places[next].next_sibling = self.places[index].first_child;
+                self.places[index].first_child = Some(next);
+                next
+            });
+        }
+
+        Ok(index)
+    }
+
+    /// Returns the number of tracked places, i.e., those for which a value can be stored.
+    pub fn tracked_places(&self) -> usize {
+        self.value_count
+    }
+
+    /// Applies a single projection element, yielding the corresponding child.
+    pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
+        self.projections.get(&(place, elem)).copied()
+    }
+
+    /// Locates the given place, if it exists in the tree.
+    pub fn find(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
+        let mut index = *self.locals.get(place.local)?.as_ref()?;
+
+        for &elem in place.projection {
+            index = self.apply(index, elem.try_into().ok()?)?;
+        }
+
+        Some(index)
+    }
+
+    /// Iterate over all direct children.
+    pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
+        Children::new(self, parent)
+    }
+
+    /// Invoke a function on the given place and all descendants.
+    pub fn preorder_invoke(&self, root: PlaceIndex, f: &mut impl FnMut(PlaceIndex)) {
+        f(root);
+        for child in self.children(root) {
+            self.preorder_invoke(child, f);
+        }
+    }
+}
+
+/// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`].
+///
+/// Together, `first_child` and `next_sibling` form an intrusive linked list, which is used to
+/// model a tree structure (a replacement for a member like `children: Vec<PlaceIndex>`).
+#[derive(Debug)]
+struct PlaceInfo {
+    /// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
+    value_index: Option<ValueIndex>,
+
+    /// The projection used to go from parent to this node (only None for root).
+    proj_elem: Option<TrackElem>,
+
+    /// The left-most child.
+    first_child: Option<PlaceIndex>,
+
+    /// Index of the sibling to the right of this node.
+    next_sibling: Option<PlaceIndex>,
+}
+
+impl PlaceInfo {
+    fn new(proj_elem: Option<TrackElem>) -> Self {
+        Self { next_sibling: None, first_child: None, proj_elem, value_index: None }
+    }
+}
+
+struct Children<'a> {
+    map: &'a Map,
+    next: Option<PlaceIndex>,
+}
+
+impl<'a> Children<'a> {
+    fn new(map: &'a Map, parent: PlaceIndex) -> Self {
+        Self { map, next: map.places[parent].first_child }
+    }
+}
+
+impl<'a> Iterator for Children<'a> {
+    type Item = PlaceIndex;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.next {
+            Some(child) => {
+                self.next = self.map.places[child].next_sibling;
+                Some(child)
+            }
+            None => None,
+        }
+    }
+}
+
+/// Used as the result of an operand or r-value.
+pub enum ValueOrPlace<V> {
+    Value(V),
+    Place(PlaceIndex),
+}
+
+impl<V: HasTop> ValueOrPlace<V> {
+    pub fn top() -> Self {
+        ValueOrPlace::Value(V::top())
+    }
+}
+
+/// The set of projection elements that can be used by a tracked place.
+///
+/// Although only field projections are currently allowed, this could change in the future.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum TrackElem {
+    Field(Field),
+}
+
+impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
+    type Error = ();
+
+    fn try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error> {
+        match value {
+            ProjectionElem::Field(field, _) => Ok(TrackElem::Field(field)),
+            _ => Err(()),
+        }
+    }
+}
+
+/// Invokes `f` on all direct fields of `ty`.
+fn iter_fields<'tcx>(
+    ty: Ty<'tcx>,
+    tcx: TyCtxt<'tcx>,
+    mut f: impl FnMut(Option<VariantIdx>, Field, Ty<'tcx>),
+) {
+    match ty.kind() {
+        ty::Tuple(list) => {
+            for (field, ty) in list.iter().enumerate() {
+                f(None, field.into(), ty);
+            }
+        }
+        ty::Adt(def, substs) => {
+            if def.is_union() {
+                return;
+            }
+            for (v_index, v_def) in def.variants().iter_enumerated() {
+                let variant = if def.is_struct() { None } else { Some(v_index) };
+                for (f_index, f_def) in v_def.fields.iter().enumerate() {
+                    let field_ty = f_def.ty(tcx, substs);
+                    let field_ty = tcx
+                        .try_normalize_erasing_regions(ty::ParamEnv::reveal_all(), field_ty)
+                        .unwrap_or(field_ty);
+                    f(variant, f_index.into(), field_ty);
+                }
+            }
+        }
+        ty::Closure(_, substs) => {
+            iter_fields(substs.as_closure().tupled_upvars_ty(), tcx, f);
+        }
+        _ => (),
+    }
+}
+
+/// Returns all locals with projections that have their reference or address taken.
+fn excluded_locals<'tcx>(body: &Body<'tcx>) -> IndexVec<Local, bool> {
+    struct Collector {
+        result: IndexVec<Local, bool>,
+    }
+
+    impl<'tcx> Visitor<'tcx> for Collector {
+        fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
+            if context.is_borrow()
+                || context.is_address_of()
+                || context.is_drop()
+                || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)
+            {
+                // A pointer to a place could be used to access other places with the same local,
+                // hence we have to exclude the local completely.
+                self.result[place.local] = true;
+            }
+        }
+    }
+
+    let mut collector = Collector { result: IndexVec::from_elem(false, &body.local_decls) };
+    collector.visit_body(body);
+    collector.result
+}
+
+/// This is used to visualize the dataflow analysis.
+impl<'tcx, T> DebugWithContext<ValueAnalysisWrapper<T>> for State<T::Value>
+where
+    T: ValueAnalysis<'tcx>,
+    T::Value: Debug,
+{
+    fn fmt_with(&self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match &self.0 {
+            StateData::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f),
+            StateData::Unreachable => write!(f, "unreachable"),
+        }
+    }
+
+    fn fmt_diff_with(
+        &self,
+        old: &Self,
+        ctxt: &ValueAnalysisWrapper<T>,
+        f: &mut Formatter<'_>,
+    ) -> std::fmt::Result {
+        match (&self.0, &old.0) {
+            (StateData::Reachable(this), StateData::Reachable(old)) => {
+                debug_with_context(this, Some(old), ctxt.0.map(), f)
+            }
+            _ => Ok(()), // Consider printing something here.
+        }
+    }
+}
+
+fn debug_with_context_rec<V: Debug + Eq>(
+    place: PlaceIndex,
+    place_str: &str,
+    new: &IndexVec<ValueIndex, V>,
+    old: Option<&IndexVec<ValueIndex, V>>,
+    map: &Map,
+    f: &mut Formatter<'_>,
+) -> std::fmt::Result {
+    if let Some(value) = map.places[place].value_index {
+        match old {
+            None => writeln!(f, "{}: {:?}", place_str, new[value])?,
+            Some(old) => {
+                if new[value] != old[value] {
+                    writeln!(f, "\u{001f}-{}: {:?}", place_str, old[value])?;
+                    writeln!(f, "\u{001f}+{}: {:?}", place_str, new[value])?;
+                }
+            }
+        }
+    }
+
+    for child in map.children(place) {
+        let info_elem = map.places[child].proj_elem.unwrap();
+        let child_place_str = match info_elem {
+            TrackElem::Field(field) => {
+                if place_str.starts_with("*") {
+                    format!("({}).{}", place_str, field.index())
+                } else {
+                    format!("{}.{}", place_str, field.index())
+                }
+            }
+        };
+        debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
+    }
+
+    Ok(())
+}
+
+fn debug_with_context<V: Debug + Eq>(
+    new: &IndexVec<ValueIndex, V>,
+    old: Option<&IndexVec<ValueIndex, V>>,
+    map: &Map,
+    f: &mut Formatter<'_>,
+) -> std::fmt::Result {
+    for (local, place) in map.locals.iter_enumerated() {
+        if let Some(place) = place {
+            debug_with_context_rec(*place, &format!("{:?}", local), new, old, map, f)?;
+        }
+    }
+    Ok(())
+}
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 4e451588845..3f5890040c3 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -266,7 +266,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
         _tcx: TyCtxt<'tcx>,
         _machine: &Self,
         _alloc_id: AllocId,
-        alloc: ConstAllocation<'tcx, Self::Provenance, Self::AllocExtra>,
+        alloc: ConstAllocation<'tcx>,
         _static_def_id: Option<DefId>,
         is_write: bool,
     ) -> InterpResult<'tcx> {
@@ -385,7 +385,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             // I don't know how return types can seem to be unsized but this happens in the
             // `type/type-unsatisfiable.rs` test.
             .filter(|ret_layout| {
-                !ret_layout.is_unsized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
+                ret_layout.is_sized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
             })
             .unwrap_or_else(|| ecx.layout_of(tcx.types.unit).unwrap());
 
@@ -471,7 +471,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             return None;
         }
 
-        self.ecx.const_to_op(&c.literal, None).ok()
+        // No span, we don't want errors to be shown.
+        self.ecx.eval_mir_constant(&c.literal, None, None).ok()
     }
 
     /// Returns the value, if any, of evaluating `place`.
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 479c4e577d4..786063d538c 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -5,7 +5,6 @@ use crate::const_prop::CanConstProp;
 use crate::const_prop::ConstPropMachine;
 use crate::const_prop::ConstPropMode;
 use crate::MirLint;
-use rustc_const_eval::const_eval::ConstEvalErr;
 use rustc_const_eval::interpret::Immediate;
 use rustc_const_eval::interpret::{
     self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup,
@@ -199,7 +198,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             // I don't know how return types can seem to be unsized but this happens in the
             // `type/type-unsatisfiable.rs` test.
             .filter(|ret_layout| {
-                !ret_layout.is_unsized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
+                ret_layout.is_sized() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
             })
             .unwrap_or_else(|| ecx.layout_of(tcx.types.unit).unwrap());
 
@@ -286,25 +285,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     }
 
     /// Returns the value, if any, of evaluating `c`.
-    fn eval_constant(
-        &mut self,
-        c: &Constant<'tcx>,
-        _source_info: SourceInfo,
-    ) -> Option<OpTy<'tcx>> {
+    fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
         // FIXME we need to revisit this for #67176
         if c.needs_subst() {
             return None;
         }
 
-        match self.ecx.const_to_op(&c.literal, None) {
-            Ok(op) => Some(op),
-            Err(error) => {
-                let tcx = self.ecx.tcx.at(c.span);
-                let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
-                err.report_as_error(tcx, "erroneous constant used");
-                None
-            }
-        }
+        self.use_ecx(source_info, |this| this.ecx.eval_mir_constant(&c.literal, Some(c.span), None))
     }
 
     /// Returns the value, if any, of evaluating `place`.
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
new file mode 100644
index 00000000000..e9027387413
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -0,0 +1,530 @@
+//! A constant propagation optimization pass based on dataflow analysis.
+//!
+//! Currently, this pass only propagates scalar values.
+
+use rustc_const_eval::interpret::{ConstValue, ImmTy, Immediate, InterpCx, Scalar};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::mir::visit::{MutVisitor, Visitor};
+use rustc_middle::mir::*;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, ValueOrPlace};
+use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects};
+use rustc_span::DUMMY_SP;
+
+use crate::MirPass;
+
+// These constants are somewhat random guesses and have not been optimized.
+// If `tcx.sess.mir_opt_level() >= 4`, we ignore the limits (this can become very expensive).
+const BLOCK_LIMIT: usize = 100;
+const PLACE_LIMIT: usize = 100;
+
+pub struct DataflowConstProp;
+
+impl<'tcx> MirPass<'tcx> for DataflowConstProp {
+    fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
+        sess.mir_opt_level() >= 3
+    }
+
+    #[instrument(skip_all level = "debug")]
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+        if tcx.sess.mir_opt_level() < 4 && body.basic_blocks.len() > BLOCK_LIMIT {
+            debug!("aborted dataflow const prop due too many basic blocks");
+            return;
+        }
+
+        // Decide which places to track during the analysis.
+        let map = Map::from_filter(tcx, body, Ty::is_scalar);
+
+        // We want to have a somewhat linear runtime w.r.t. the number of statements/terminators.
+        // Let's call this number `n`. Dataflow analysis has `O(h*n)` transfer function
+        // applications, where `h` is the height of the lattice. Because the height of our lattice
+        // is linear w.r.t. the number of tracked places, this is `O(tracked_places * n)`. However,
+        // because every transfer function application could traverse the whole map, this becomes
+        // `O(num_nodes * tracked_places * n)` in terms of time complexity. Since the number of
+        // map nodes is strongly correlated to the number of tracked places, this becomes more or
+        // less `O(n)` if we place a constant limit on the number of tracked places.
+        if tcx.sess.mir_opt_level() < 4 && map.tracked_places() > PLACE_LIMIT {
+            debug!("aborted dataflow const prop due to too many tracked places");
+            return;
+        }
+
+        // Perform the actual dataflow analysis.
+        let analysis = ConstAnalysis::new(tcx, body, map);
+        let results = debug_span!("analyze")
+            .in_scope(|| analysis.wrap().into_engine(tcx, body).iterate_to_fixpoint());
+
+        // Collect results and patch the body afterwards.
+        let mut visitor = CollectAndPatch::new(tcx, &results.analysis.0.map);
+        debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor));
+        debug_span!("patch").in_scope(|| visitor.visit_body(body));
+    }
+}
+
+struct ConstAnalysis<'tcx> {
+    map: Map,
+    tcx: TyCtxt<'tcx>,
+    ecx: InterpCx<'tcx, 'tcx, DummyMachine>,
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
+    type Value = FlatSet<ScalarTy<'tcx>>;
+
+    const NAME: &'static str = "ConstAnalysis";
+
+    fn map(&self) -> &Map {
+        &self.map
+    }
+
+    fn handle_assign(
+        &self,
+        target: Place<'tcx>,
+        rvalue: &Rvalue<'tcx>,
+        state: &mut State<Self::Value>,
+    ) {
+        match rvalue {
+            Rvalue::CheckedBinaryOp(op, box (left, right)) => {
+                let target = self.map().find(target.as_ref());
+                if let Some(target) = target {
+                    // We should not track any projections other than
+                    // what is overwritten below, but just in case...
+                    state.flood_idx(target, self.map());
+                }
+
+                let value_target = target
+                    .and_then(|target| self.map().apply(target, TrackElem::Field(0_u32.into())));
+                let overflow_target = target
+                    .and_then(|target| self.map().apply(target, TrackElem::Field(1_u32.into())));
+
+                if value_target.is_some() || overflow_target.is_some() {
+                    let (val, overflow) = self.binary_op(state, *op, left, right);
+
+                    if let Some(value_target) = value_target {
+                        state.assign_idx(value_target, ValueOrPlace::Value(val), self.map());
+                    }
+                    if let Some(overflow_target) = overflow_target {
+                        let overflow = match overflow {
+                            FlatSet::Top => FlatSet::Top,
+                            FlatSet::Elem(overflow) => {
+                                if overflow {
+                                    // Overflow cannot be reliably propagated. See: https://github.com/rust-lang/rust/pull/101168#issuecomment-1288091446
+                                    FlatSet::Top
+                                } else {
+                                    self.wrap_scalar(Scalar::from_bool(false), self.tcx.types.bool)
+                                }
+                            }
+                            FlatSet::Bottom => FlatSet::Bottom,
+                        };
+                        state.assign_idx(
+                            overflow_target,
+                            ValueOrPlace::Value(overflow),
+                            self.map(),
+                        );
+                    }
+                }
+            }
+            _ => self.super_assign(target, rvalue, state),
+        }
+    }
+
+    fn handle_rvalue(
+        &self,
+        rvalue: &Rvalue<'tcx>,
+        state: &mut State<Self::Value>,
+    ) -> ValueOrPlace<Self::Value> {
+        match rvalue {
+            Rvalue::Cast(
+                kind @ (CastKind::IntToInt
+                | CastKind::FloatToInt
+                | CastKind::FloatToFloat
+                | CastKind::IntToFloat),
+                operand,
+                ty,
+            ) => match self.eval_operand(operand, state) {
+                FlatSet::Elem(op) => match kind {
+                    CastKind::IntToInt | CastKind::IntToFloat => {
+                        self.ecx.int_to_int_or_float(&op, *ty)
+                    }
+                    CastKind::FloatToInt | CastKind::FloatToFloat => {
+                        self.ecx.float_to_float_or_int(&op, *ty)
+                    }
+                    _ => unreachable!(),
+                }
+                .map(|result| ValueOrPlace::Value(self.wrap_immediate(result, *ty)))
+                .unwrap_or(ValueOrPlace::top()),
+                _ => ValueOrPlace::top(),
+            },
+            Rvalue::BinaryOp(op, box (left, right)) => {
+                // Overflows must be ignored here.
+                let (val, _overflow) = self.binary_op(state, *op, left, right);
+                ValueOrPlace::Value(val)
+            }
+            Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) {
+                FlatSet::Elem(value) => self
+                    .ecx
+                    .unary_op(*op, &value)
+                    .map(|val| ValueOrPlace::Value(self.wrap_immty(val)))
+                    .unwrap_or(ValueOrPlace::Value(FlatSet::Top)),
+                FlatSet::Bottom => ValueOrPlace::Value(FlatSet::Bottom),
+                FlatSet::Top => ValueOrPlace::Value(FlatSet::Top),
+            },
+            _ => self.super_rvalue(rvalue, state),
+        }
+    }
+
+    fn handle_constant(
+        &self,
+        constant: &Constant<'tcx>,
+        _state: &mut State<Self::Value>,
+    ) -> Self::Value {
+        constant
+            .literal
+            .eval(self.tcx, self.param_env)
+            .try_to_scalar()
+            .map(|value| FlatSet::Elem(ScalarTy(value, constant.ty())))
+            .unwrap_or(FlatSet::Top)
+    }
+
+    fn handle_switch_int(
+        &self,
+        discr: &Operand<'tcx>,
+        apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
+    ) {
+        // FIXME: The dataflow framework only provides the state if we call `apply()`, which makes
+        // this more inefficient than it has to be.
+        let mut discr_value = None;
+        let mut handled = false;
+        apply_edge_effects.apply(|state, target| {
+            let discr_value = match discr_value {
+                Some(value) => value,
+                None => {
+                    let value = match self.handle_operand(discr, state) {
+                        ValueOrPlace::Value(value) => value,
+                        ValueOrPlace::Place(place) => state.get_idx(place, self.map()),
+                    };
+                    let result = match value {
+                        FlatSet::Top => FlatSet::Top,
+                        FlatSet::Elem(ScalarTy(scalar, _)) => {
+                            let int = scalar.assert_int();
+                            FlatSet::Elem(int.assert_bits(int.size()))
+                        }
+                        FlatSet::Bottom => FlatSet::Bottom,
+                    };
+                    discr_value = Some(result);
+                    result
+                }
+            };
+
+            let FlatSet::Elem(choice) = discr_value else {
+                // Do nothing if we don't know which branch will be taken.
+                return
+            };
+
+            if target.value.map(|n| n == choice).unwrap_or(!handled) {
+                // Branch is taken. Has no effect on state.
+                handled = true;
+            } else {
+                // Branch is not taken.
+                state.mark_unreachable();
+            }
+        })
+    }
+}
+
+#[derive(Clone, PartialEq, Eq)]
+struct ScalarTy<'tcx>(Scalar, Ty<'tcx>);
+
+impl<'tcx> std::fmt::Debug for ScalarTy<'tcx> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        // This is used for dataflow visualization, so we return something more concise.
+        std::fmt::Display::fmt(&ConstantKind::Val(ConstValue::Scalar(self.0), self.1), f)
+    }
+}
+
+impl<'tcx> ConstAnalysis<'tcx> {
+    pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, map: Map) -> Self {
+        let param_env = tcx.param_env(body.source.def_id());
+        Self {
+            map,
+            tcx,
+            ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine),
+            param_env: param_env,
+        }
+    }
+
+    fn binary_op(
+        &self,
+        state: &mut State<FlatSet<ScalarTy<'tcx>>>,
+        op: BinOp,
+        left: &Operand<'tcx>,
+        right: &Operand<'tcx>,
+    ) -> (FlatSet<ScalarTy<'tcx>>, FlatSet<bool>) {
+        let left = self.eval_operand(left, state);
+        let right = self.eval_operand(right, state);
+        match (left, right) {
+            (FlatSet::Elem(left), FlatSet::Elem(right)) => {
+                match self.ecx.overflowing_binary_op(op, &left, &right) {
+                    Ok((val, overflow, ty)) => (self.wrap_scalar(val, ty), FlatSet::Elem(overflow)),
+                    _ => (FlatSet::Top, FlatSet::Top),
+                }
+            }
+            (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
+            (_, _) => {
+                // Could attempt some algebraic simplifcations here.
+                (FlatSet::Top, FlatSet::Top)
+            }
+        }
+    }
+
+    fn eval_operand(
+        &self,
+        op: &Operand<'tcx>,
+        state: &mut State<FlatSet<ScalarTy<'tcx>>>,
+    ) -> FlatSet<ImmTy<'tcx>> {
+        let value = match self.handle_operand(op, state) {
+            ValueOrPlace::Value(value) => value,
+            ValueOrPlace::Place(place) => state.get_idx(place, &self.map),
+        };
+        match value {
+            FlatSet::Top => FlatSet::Top,
+            FlatSet::Elem(ScalarTy(scalar, ty)) => self
+                .tcx
+                .layout_of(self.param_env.and(ty))
+                .map(|layout| FlatSet::Elem(ImmTy::from_scalar(scalar, layout)))
+                .unwrap_or(FlatSet::Top),
+            FlatSet::Bottom => FlatSet::Bottom,
+        }
+    }
+
+    fn wrap_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
+        FlatSet::Elem(ScalarTy(scalar, ty))
+    }
+
+    fn wrap_immediate(&self, imm: Immediate, ty: Ty<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
+        match imm {
+            Immediate::Scalar(scalar) => self.wrap_scalar(scalar, ty),
+            _ => FlatSet::Top,
+        }
+    }
+
+    fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
+        self.wrap_immediate(*val, val.layout.ty)
+    }
+}
+
+struct CollectAndPatch<'tcx, 'map> {
+    tcx: TyCtxt<'tcx>,
+    map: &'map Map,
+
+    /// For a given MIR location, this stores the values of the operands used by that location. In
+    /// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are
+    /// properly captured. (This may become UB soon, but it is currently emitted even by safe code.)
+    before_effect: FxHashMap<(Location, Place<'tcx>), ScalarTy<'tcx>>,
+
+    /// Stores the assigned values for assignments where the Rvalue is constant.
+    assignments: FxHashMap<Location, ScalarTy<'tcx>>,
+}
+
+impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> {
+    fn new(tcx: TyCtxt<'tcx>, map: &'map Map) -> Self {
+        Self { tcx, map, before_effect: FxHashMap::default(), assignments: FxHashMap::default() }
+    }
+
+    fn make_operand(&self, scalar: ScalarTy<'tcx>) -> Operand<'tcx> {
+        Operand::Constant(Box::new(Constant {
+            span: DUMMY_SP,
+            user_ty: None,
+            literal: ConstantKind::Val(ConstValue::Scalar(scalar.0), scalar.1),
+        }))
+    }
+}
+
+impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map> {
+    type FlowState = State<FlatSet<ScalarTy<'tcx>>>;
+
+    fn visit_statement_before_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        statement: &'mir Statement<'tcx>,
+        location: Location,
+    ) {
+        match &statement.kind {
+            StatementKind::Assign(box (_, rvalue)) => {
+                OperandCollector { state, visitor: self }.visit_rvalue(rvalue, location);
+            }
+            _ => (),
+        }
+    }
+
+    fn visit_statement_after_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        statement: &'mir Statement<'tcx>,
+        location: Location,
+    ) {
+        match statement.kind {
+            StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(_)))) => {
+                // Don't overwrite the assignment if it already uses a constant (to keep the span).
+            }
+            StatementKind::Assign(box (place, _)) => match state.get(place.as_ref(), self.map) {
+                FlatSet::Top => (),
+                FlatSet::Elem(value) => {
+                    self.assignments.insert(location, value);
+                }
+                FlatSet::Bottom => {
+                    // This assignment is either unreachable, or an uninitialized value is assigned.
+                }
+            },
+            _ => (),
+        }
+    }
+
+    fn visit_terminator_before_primary_effect(
+        &mut self,
+        state: &Self::FlowState,
+        terminator: &'mir Terminator<'tcx>,
+        location: Location,
+    ) {
+        OperandCollector { state, visitor: self }.visit_terminator(terminator, location);
+    }
+}
+
+impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> {
+    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
+        if let Some(value) = self.assignments.get(&location) {
+            match &mut statement.kind {
+                StatementKind::Assign(box (_, rvalue)) => {
+                    *rvalue = Rvalue::Use(self.make_operand(value.clone()));
+                }
+                _ => bug!("found assignment info for non-assign statement"),
+            }
+        } else {
+            self.super_statement(statement, location);
+        }
+    }
+
+    fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
+        match operand {
+            Operand::Copy(place) | Operand::Move(place) => {
+                if let Some(value) = self.before_effect.get(&(location, *place)) {
+                    *operand = self.make_operand(value.clone());
+                }
+            }
+            _ => (),
+        }
+    }
+}
+
+struct OperandCollector<'tcx, 'map, 'a> {
+    state: &'a State<FlatSet<ScalarTy<'tcx>>>,
+    visitor: &'a mut CollectAndPatch<'tcx, 'map>,
+}
+
+impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> {
+    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
+        match operand {
+            Operand::Copy(place) | Operand::Move(place) => {
+                match self.state.get(place.as_ref(), self.visitor.map) {
+                    FlatSet::Top => (),
+                    FlatSet::Elem(value) => {
+                        self.visitor.before_effect.insert((location, *place), value);
+                    }
+                    FlatSet::Bottom => (),
+                }
+            }
+            _ => (),
+        }
+    }
+}
+
+struct DummyMachine;
+
+impl<'mir, 'tcx> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine {
+    rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>);
+    type MemoryKind = !;
+    const PANIC_ON_ALLOC_FAIL: bool = true;
+
+    fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        unimplemented!()
+    }
+
+    fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        unimplemented!()
+    }
+
+    fn find_mir_or_eval_fn(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _instance: ty::Instance<'tcx>,
+        _abi: rustc_target::spec::abi::Abi,
+        _args: &[rustc_const_eval::interpret::OpTy<'tcx, Self::Provenance>],
+        _destination: &rustc_const_eval::interpret::PlaceTy<'tcx, Self::Provenance>,
+        _target: Option<BasicBlock>,
+        _unwind: rustc_const_eval::interpret::StackPopUnwind,
+    ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> {
+        unimplemented!()
+    }
+
+    fn call_intrinsic(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _instance: ty::Instance<'tcx>,
+        _args: &[rustc_const_eval::interpret::OpTy<'tcx, Self::Provenance>],
+        _destination: &rustc_const_eval::interpret::PlaceTy<'tcx, Self::Provenance>,
+        _target: Option<BasicBlock>,
+        _unwind: rustc_const_eval::interpret::StackPopUnwind,
+    ) -> interpret::InterpResult<'tcx> {
+        unimplemented!()
+    }
+
+    fn assert_panic(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _msg: &rustc_middle::mir::AssertMessage<'tcx>,
+        _unwind: Option<BasicBlock>,
+    ) -> interpret::InterpResult<'tcx> {
+        unimplemented!()
+    }
+
+    fn binary_ptr_op(
+        _ecx: &InterpCx<'mir, 'tcx, Self>,
+        _bin_op: BinOp,
+        _left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
+        _right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
+    ) -> interpret::InterpResult<'tcx, (interpret::Scalar<Self::Provenance>, bool, Ty<'tcx>)> {
+        throw_unsup!(Unsupported("".into()))
+    }
+
+    fn expose_ptr(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _ptr: interpret::Pointer<Self::Provenance>,
+    ) -> interpret::InterpResult<'tcx> {
+        unimplemented!()
+    }
+
+    fn init_frame_extra(
+        _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        _frame: rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance>,
+    ) -> interpret::InterpResult<
+        'tcx,
+        rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
+    > {
+        unimplemented!()
+    }
+
+    fn stack<'a>(
+        _ecx: &'a InterpCx<'mir, 'tcx, Self>,
+    ) -> &'a [rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>]
+    {
+        unimplemented!()
+    }
+
+    fn stack_mut<'a>(
+        _ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
+    ) -> &'a mut Vec<
+        rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
+    > {
+        unimplemented!()
+    }
+}
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 4791be1306c..93200b28830 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -1,5 +1,6 @@
 #![allow(rustc::potential_query_instability)]
 #![feature(box_patterns)]
+#![feature(drain_filter)]
 #![feature(let_chains)]
 #![feature(map_try_insert)]
 #![feature(min_specialization)]
@@ -54,6 +55,7 @@ mod const_goto;
 mod const_prop;
 mod const_prop_lint;
 mod coverage;
+mod dataflow_const_prop;
 mod dead_store_elimination;
 mod deaggregator;
 mod deduce_param_attrs;
@@ -91,6 +93,7 @@ pub mod simplify;
 mod simplify_branches;
 mod simplify_comparison_integral;
 mod simplify_try;
+mod sroa;
 mod uninhabited_enum_branching;
 mod unreachable_prop;
 
@@ -217,19 +220,18 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
 
     // Additionally, tuple struct/variant constructors have MIR, but
     // they don't have a BodyId, so we need to build them separately.
-    struct GatherCtors<'a, 'tcx> {
-        tcx: TyCtxt<'tcx>,
+    struct GatherCtors<'a> {
         set: &'a mut FxIndexSet<LocalDefId>,
     }
-    impl<'tcx> Visitor<'tcx> for GatherCtors<'_, 'tcx> {
+    impl<'tcx> Visitor<'tcx> for GatherCtors<'_> {
         fn visit_variant_data(&mut self, v: &'tcx hir::VariantData<'tcx>) {
-            if let hir::VariantData::Tuple(_, hir_id) = *v {
-                self.set.insert(self.tcx.hir().local_def_id(hir_id));
+            if let hir::VariantData::Tuple(_, _, def_id) = *v {
+                self.set.insert(def_id);
             }
             intravisit::walk_struct_def(self, v)
         }
     }
-    tcx.hir().visit_all_item_likes_in_crate(&mut GatherCtors { tcx, set: &mut set });
+    tcx.hir().visit_all_item_likes_in_crate(&mut GatherCtors { set: &mut set });
 
     set
 }
@@ -561,6 +563,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             &remove_zsts::RemoveZsts,
             &const_goto::ConstGoto,
             &remove_unneeded_drops::RemoveUnneededDrops,
+            &sroa::ScalarReplacementOfAggregates,
             &match_branches::MatchBranchSimplification,
             // inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
             &multiple_return_terminators::MultipleReturnTerminators,
@@ -569,6 +572,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             //
             // FIXME(#70073): This pass is responsible for both optimization as well as some lints.
             &const_prop::ConstProp,
+            &dataflow_const_prop::DataflowConstProp,
             //
             // Const-prop runs unconditionally, but doesn't mutate the MIR at mir-opt-level=0.
             &const_debuginfo::ConstDebugInfo,
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
new file mode 100644
index 00000000000..558a372fb1e
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -0,0 +1,348 @@
+use crate::MirPass;
+use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::visit::*;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+
+pub struct ScalarReplacementOfAggregates;
+
+impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
+    fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
+        sess.mir_opt_level() >= 3
+    }
+
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+        let escaping = escaping_locals(&*body);
+        debug!(?escaping);
+        let replacements = compute_flattening(tcx, body, escaping);
+        debug!(?replacements);
+        replace_flattened_locals(tcx, body, replacements);
+    }
+}
+
+/// Identify all locals that are not eligible for SROA.
+///
+/// There are 3 cases:
+/// - the aggegated local is used or passed to other code (function parameters and arguments);
+/// - the locals is a union or an enum;
+/// - the local's address is taken, and thus the relative addresses of the fields are observable to
+///   client code.
+fn escaping_locals(body: &Body<'_>) -> BitSet<Local> {
+    let mut set = BitSet::new_empty(body.local_decls.len());
+    set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count));
+    for (local, decl) in body.local_decls().iter_enumerated() {
+        if decl.ty.is_union() || decl.ty.is_enum() {
+            set.insert(local);
+        }
+    }
+    let mut visitor = EscapeVisitor { set };
+    visitor.visit_body(body);
+    return visitor.set;
+
+    struct EscapeVisitor {
+        set: BitSet<Local>,
+    }
+
+    impl<'tcx> Visitor<'tcx> for EscapeVisitor {
+        fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) {
+            self.set.insert(local);
+        }
+
+        fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+            // Mirror the implementation in PreFlattenVisitor.
+            if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
+                return;
+            }
+            self.super_place(place, context, location);
+        }
+
+        fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+            if let Rvalue::AddressOf(.., place) | Rvalue::Ref(.., place) = rvalue {
+                if !place.is_indirect() {
+                    // Raw pointers may be used to access anything inside the enclosing place.
+                    self.set.insert(place.local);
+                    return;
+                }
+            }
+            self.super_rvalue(rvalue, location)
+        }
+
+        fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+            if let StatementKind::StorageLive(..)
+            | StatementKind::StorageDead(..)
+            | StatementKind::Deinit(..) = statement.kind
+            {
+                // Storage statements are expanded in run_pass.
+                return;
+            }
+            self.super_statement(statement, location)
+        }
+
+        fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+            // Drop implicitly calls `drop_in_place`, which takes a `&mut`.
+            // This implies that `Drop` implicitly takes the address of the place.
+            if let TerminatorKind::Drop { place, .. }
+            | TerminatorKind::DropAndReplace { place, .. } = terminator.kind
+            {
+                if !place.is_indirect() {
+                    // Raw pointers may be used to access anything inside the enclosing place.
+                    self.set.insert(place.local);
+                    return;
+                }
+            }
+            self.super_terminator(terminator, location);
+        }
+
+        // We ignore anything that happens in debuginfo, since we expand it using
+        // `VarDebugInfoContents::Composite`.
+        fn visit_var_debug_info(&mut self, _: &VarDebugInfo<'tcx>) {}
+    }
+}
+
+#[derive(Default, Debug)]
+struct ReplacementMap<'tcx> {
+    fields: FxIndexMap<PlaceRef<'tcx>, Local>,
+}
+
+/// Compute the replacement of flattened places into locals.
+///
+/// For each eligible place, we assign a new local to each accessed field.
+/// The replacement will be done later in `ReplacementVisitor`.
+fn compute_flattening<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &mut Body<'tcx>,
+    escaping: BitSet<Local>,
+) -> ReplacementMap<'tcx> {
+    let mut visitor = PreFlattenVisitor {
+        tcx,
+        escaping,
+        local_decls: &mut body.local_decls,
+        map: Default::default(),
+    };
+    for (block, bbdata) in body.basic_blocks.iter_enumerated() {
+        visitor.visit_basic_block_data(block, bbdata);
+    }
+    return visitor.map;
+
+    struct PreFlattenVisitor<'tcx, 'll> {
+        tcx: TyCtxt<'tcx>,
+        local_decls: &'ll mut LocalDecls<'tcx>,
+        escaping: BitSet<Local>,
+        map: ReplacementMap<'tcx>,
+    }
+
+    impl<'tcx, 'll> PreFlattenVisitor<'tcx, 'll> {
+        fn create_place(&mut self, place: PlaceRef<'tcx>) {
+            if self.escaping.contains(place.local) {
+                return;
+            }
+
+            match self.map.fields.entry(place) {
+                IndexEntry::Occupied(_) => {}
+                IndexEntry::Vacant(v) => {
+                    let ty = place.ty(&*self.local_decls, self.tcx).ty;
+                    let local = self.local_decls.push(LocalDecl {
+                        ty,
+                        user_ty: None,
+                        ..self.local_decls[place.local].clone()
+                    });
+                    v.insert(local);
+                }
+            }
+        }
+    }
+
+    impl<'tcx, 'll> Visitor<'tcx> for PreFlattenVisitor<'tcx, 'll> {
+        fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) {
+            if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
+                let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
+                self.create_place(pr)
+            }
+        }
+    }
+}
+
+/// Perform the replacement computed by `compute_flattening`.
+fn replace_flattened_locals<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &mut Body<'tcx>,
+    replacements: ReplacementMap<'tcx>,
+) {
+    let mut all_dead_locals = BitSet::new_empty(body.local_decls.len());
+    for p in replacements.fields.keys() {
+        all_dead_locals.insert(p.local);
+    }
+    debug!(?all_dead_locals);
+    if all_dead_locals.is_empty() {
+        return;
+    }
+
+    let mut fragments = IndexVec::new();
+    for (k, v) in &replacements.fields {
+        fragments.ensure_contains_elem(k.local, || Vec::new());
+        fragments[k.local].push((&k.projection[..], *v));
+    }
+    debug!(?fragments);
+
+    let mut visitor = ReplacementVisitor {
+        tcx,
+        local_decls: &body.local_decls,
+        replacements,
+        all_dead_locals,
+        fragments,
+    };
+    for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
+        visitor.visit_basic_block_data(bb, data);
+    }
+    for scope in &mut body.source_scopes {
+        visitor.visit_source_scope_data(scope);
+    }
+    for (index, annotation) in body.user_type_annotations.iter_enumerated_mut() {
+        visitor.visit_user_type_annotation(index, annotation);
+    }
+    for var_debug_info in &mut body.var_debug_info {
+        visitor.visit_var_debug_info(var_debug_info);
+    }
+}
+
+struct ReplacementVisitor<'tcx, 'll> {
+    tcx: TyCtxt<'tcx>,
+    /// This is only used to compute the type for `VarDebugInfoContents::Composite`.
+    local_decls: &'ll LocalDecls<'tcx>,
+    /// Work to do.
+    replacements: ReplacementMap<'tcx>,
+    /// This is used to check that we are not leaving references to replaced locals behind.
+    all_dead_locals: BitSet<Local>,
+    /// Pre-computed list of all "new" locals for each "old" local.  This is used to expand storage
+    /// and deinit statement and debuginfo.
+    fragments: IndexVec<Local, Vec<(&'tcx [PlaceElem<'tcx>], Local)>>,
+}
+
+impl<'tcx, 'll> ReplacementVisitor<'tcx, 'll> {
+    fn gather_debug_info_fragments(
+        &self,
+        place: PlaceRef<'tcx>,
+    ) -> Vec<VarDebugInfoFragment<'tcx>> {
+        let mut fragments = Vec::new();
+        let parts = &self.fragments[place.local];
+        for (proj, replacement_local) in parts {
+            if proj.starts_with(place.projection) {
+                fragments.push(VarDebugInfoFragment {
+                    projection: proj[place.projection.len()..].to_vec(),
+                    contents: Place::from(*replacement_local),
+                });
+            }
+        }
+        fragments
+    }
+
+    fn replace_place(&self, place: PlaceRef<'tcx>) -> Option<Place<'tcx>> {
+        if let &[PlaceElem::Field(..), ref rest @ ..] = place.projection {
+            let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
+            let local = self.replacements.fields.get(&pr)?;
+            Some(Place { local: *local, projection: self.tcx.intern_place_elems(&rest) })
+        } else {
+            None
+        }
+    }
+}
+
+impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
+        if let StatementKind::StorageLive(..)
+        | StatementKind::StorageDead(..)
+        | StatementKind::Deinit(..) = statement.kind
+        {
+            // Storage statements are expanded in run_pass.
+            return;
+        }
+        self.super_statement(statement, location)
+    }
+
+    fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
+        if let Some(repl) = self.replace_place(place.as_ref()) {
+            *place = repl
+        } else {
+            self.super_place(place, context, location)
+        }
+    }
+
+    fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
+        match &mut var_debug_info.value {
+            VarDebugInfoContents::Place(ref mut place) => {
+                if let Some(repl) = self.replace_place(place.as_ref()) {
+                    *place = repl;
+                } else if self.all_dead_locals.contains(place.local) {
+                    let ty = place.ty(self.local_decls, self.tcx).ty;
+                    let fragments = self.gather_debug_info_fragments(place.as_ref());
+                    var_debug_info.value = VarDebugInfoContents::Composite { ty, fragments };
+                }
+            }
+            VarDebugInfoContents::Composite { ty: _, ref mut fragments } => {
+                let mut new_fragments = Vec::new();
+                fragments
+                    .drain_filter(|fragment| {
+                        if let Some(repl) = self.replace_place(fragment.contents.as_ref()) {
+                            fragment.contents = repl;
+                            true
+                        } else if self.all_dead_locals.contains(fragment.contents.local) {
+                            let frg = self.gather_debug_info_fragments(fragment.contents.as_ref());
+                            new_fragments.extend(frg.into_iter().map(|mut f| {
+                                f.projection.splice(0..0, fragment.projection.iter().copied());
+                                f
+                            }));
+                            false
+                        } else {
+                            true
+                        }
+                    })
+                    .for_each(drop);
+                fragments.extend(new_fragments);
+            }
+            VarDebugInfoContents::Const(_) => {}
+        }
+    }
+
+    fn visit_basic_block_data(&mut self, bb: BasicBlock, bbdata: &mut BasicBlockData<'tcx>) {
+        self.super_basic_block_data(bb, bbdata);
+
+        #[derive(Debug)]
+        enum Stmt {
+            StorageLive,
+            StorageDead,
+            Deinit,
+        }
+
+        bbdata.expand_statements(|stmt| {
+            let source_info = stmt.source_info;
+            let (stmt, origin_local) = match &stmt.kind {
+                StatementKind::StorageLive(l) => (Stmt::StorageLive, *l),
+                StatementKind::StorageDead(l) => (Stmt::StorageDead, *l),
+                StatementKind::Deinit(p) if let Some(l) = p.as_local() => (Stmt::Deinit, l),
+                _ => return None,
+            };
+            if !self.all_dead_locals.contains(origin_local) {
+                return None;
+            }
+            let final_locals = self.fragments.get(origin_local)?;
+            Some(final_locals.iter().map(move |&(_, l)| {
+                let kind = match stmt {
+                    Stmt::StorageLive => StatementKind::StorageLive(l),
+                    Stmt::StorageDead => StatementKind::StorageDead(l),
+                    Stmt::Deinit => StatementKind::Deinit(Box::new(l.into())),
+                };
+                Statement { source_info, kind }
+            }))
+        });
+    }
+
+    fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
+        assert!(!self.all_dead_locals.contains(*local));
+    }
+}
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 58ddb807059..cdf8011d4f5 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -456,7 +456,7 @@ fn collect_items_rec<'tcx>(
             recursion_depth_reset = None;
 
             if let Ok(alloc) = tcx.eval_static_initializer(def_id) {
-                for &id in alloc.inner().provenance().values() {
+                for &id in alloc.inner().provenance().ptrs().values() {
                     collect_miri(tcx, id, &mut neighbors);
                 }
             }
@@ -773,7 +773,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
                     match self.tcx.const_eval_resolve(param_env, ct.expand(), None) {
                         // The `monomorphize` call should have evaluated that constant already.
                         Ok(val) => val,
-                        Err(ErrorHandled::Reported(_) | ErrorHandled::Linted) => return,
+                        Err(ErrorHandled::Reported(_)) => return,
                         Err(ErrorHandled::TooGeneric) => span_bug!(
                             self.body.source_info(location).span,
                             "collection encountered polymorphic constant: {:?}",
@@ -788,7 +788,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
                 match self.tcx.const_eval_resolve(param_env, uv, None) {
                     // The `monomorphize` call should have evaluated that constant already.
                     Ok(val) => val,
-                    Err(ErrorHandled::Reported(_) | ErrorHandled::Linted) => return,
+                    Err(ErrorHandled::Reported(_)) => return,
                     Err(ErrorHandled::TooGeneric) => span_bug!(
                         self.body.source_info(location).span,
                         "collection encountered polymorphic constant: {:?}",
@@ -1404,7 +1404,7 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte
         }
         GlobalAlloc::Memory(alloc) => {
             trace!("collecting {:?} with {:#?}", alloc_id, alloc);
-            for &inner in alloc.inner().provenance().values() {
+            for &inner in alloc.inner().provenance().ptrs().values() {
                 rustc_data_structures::stack::ensure_sufficient_stack(|| {
                     collect_miri(tcx, inner, output);
                 });
@@ -1443,7 +1443,7 @@ fn collect_const_value<'tcx>(
     match value {
         ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_miri(tcx, ptr.provenance, output),
         ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => {
-            for &id in alloc.inner().provenance().values() {
+            for &id in alloc.inner().provenance().ptrs().values() {
                 collect_miri(tcx, id, output);
             }
         }
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index d59982f7063..724d92254a4 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -305,61 +305,6 @@ pub(crate) struct FloatLiteralRequiresIntegerPart {
 }
 
 #[derive(Diagnostic)]
-#[diag(parser_invalid_int_literal_width)]
-#[help]
-pub(crate) struct InvalidIntLiteralWidth {
-    #[primary_span]
-    pub span: Span,
-    pub width: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_invalid_num_literal_base_prefix)]
-#[note]
-pub(crate) struct InvalidNumLiteralBasePrefix {
-    #[primary_span]
-    #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")]
-    pub span: Span,
-    pub fixed: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_invalid_num_literal_suffix)]
-#[help]
-pub(crate) struct InvalidNumLiteralSuffix {
-    #[primary_span]
-    #[label]
-    pub span: Span,
-    pub suffix: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_invalid_float_literal_width)]
-#[help]
-pub(crate) struct InvalidFloatLiteralWidth {
-    #[primary_span]
-    pub span: Span,
-    pub width: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_invalid_float_literal_suffix)]
-#[help]
-pub(crate) struct InvalidFloatLiteralSuffix {
-    #[primary_span]
-    #[label]
-    pub span: Span,
-    pub suffix: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_int_literal_too_large)]
-pub(crate) struct IntLiteralTooLarge {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(parser_missing_semicolon_before_array)]
 pub(crate) struct MissingSemicolonBeforeArray {
     #[primary_span]
@@ -741,41 +686,6 @@ pub(crate) struct InvalidInterpolatedExpression {
 }
 
 #[derive(Diagnostic)]
-#[diag(parser_hexadecimal_float_literal_not_supported)]
-pub(crate) struct HexadecimalFloatLiteralNotSupported {
-    #[primary_span]
-    #[label(parser_not_supported)]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_octal_float_literal_not_supported)]
-pub(crate) struct OctalFloatLiteralNotSupported {
-    #[primary_span]
-    #[label(parser_not_supported)]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_binary_float_literal_not_supported)]
-pub(crate) struct BinaryFloatLiteralNotSupported {
-    #[primary_span]
-    #[label(parser_not_supported)]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
-#[diag(parser_invalid_literal_suffix)]
-pub(crate) struct InvalidLiteralSuffix {
-    #[primary_span]
-    #[label]
-    pub span: Span,
-    // FIXME(#100717)
-    pub kind: String,
-    pub suffix: Symbol,
-}
-
-#[derive(Diagnostic)]
 #[diag(parser_invalid_literal_suffix_on_tuple_index)]
 pub(crate) struct InvalidLiteralSuffixOnTupleIndex {
     #[primary_span]
@@ -1206,6 +1116,14 @@ pub(crate) struct SelfParamNotFirst {
 }
 
 #[derive(Diagnostic)]
+#[diag(parser_invalid_identifier_with_leading_number)]
+pub(crate) struct InvalidIdentiferStartsWithNumber {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(parser_const_generic_without_braces)]
 pub(crate) struct ConstGenericWithoutBraces {
     #[primary_span]
@@ -1280,3 +1198,24 @@ pub(crate) struct DoubleColonInBound {
     #[suggestion(code = ": ", applicability = "machine-applicable")]
     pub between: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(parser_fn_ptr_with_generics)]
+pub(crate) struct FnPtrWithGenerics {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sugg: Option<FnPtrWithGenericsSugg>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+pub(crate) struct FnPtrWithGenericsSugg {
+    #[suggestion_part(code = "{snippet}")]
+    pub left: Span,
+    pub snippet: String,
+    #[suggestion_part(code = "")]
+    pub right: Span,
+    pub arity: usize,
+    pub for_param_list_exists: bool,
+}
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 645262bd2f1..f027843e6b4 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -661,6 +661,7 @@ impl<'a> StringReader<'a> {
         prefix_len: u32,
         postfix_len: u32,
     ) -> (token::LitKind, Symbol) {
+        let mut has_fatal_err = false;
         let content_start = start + BytePos(prefix_len);
         let content_end = end - BytePos(postfix_len);
         let lit_content = self.str_from_to(content_start, content_end);
@@ -672,6 +673,9 @@ impl<'a> StringReader<'a> {
                 let lo = content_start + BytePos(start);
                 let hi = lo + BytePos(end - start);
                 let span = self.mk_sp(lo, hi);
+                if err.is_fatal() {
+                    has_fatal_err = true;
+                }
                 emit_unescape_error(
                     &self.sess.span_diagnostic,
                     lit_content,
@@ -683,7 +687,14 @@ impl<'a> StringReader<'a> {
                 );
             }
         });
-        (kind, Symbol::intern(lit_content))
+
+        // We normally exclude the quotes for the symbol, but for errors we
+        // include it because it results in clearer error messages.
+        if !has_fatal_err {
+            (kind, Symbol::intern(lit_content))
+        } else {
+            (token::Err, self.symbol_from_to(start, end))
+        }
     }
 }
 
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 3dcadb4c911..c78479b098b 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -3,6 +3,7 @@
 #![feature(array_windows)]
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
+#![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 9e45656946b..612accf3e3b 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -316,8 +316,8 @@ impl<'a> Parser<'a> {
     }
 
     pub(crate) fn parse_unsuffixed_lit(&mut self) -> PResult<'a, ast::Lit> {
-        let lit = self.parse_lit()?;
-        debug!("checking if {:?} is unusuffixed", lit);
+        let lit = self.parse_ast_lit()?;
+        debug!("checking if {:?} is unsuffixed", lit);
 
         if !lit.kind.is_unsuffixed() {
             self.sess.emit_err(SuffixedLiteralInAttribute { span: lit.span });
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 1b16ecb5ec2..c8160548763 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -5,7 +5,8 @@ use rustc_ast::tokenstream::{AttrTokenTree, DelimSpan, LazyAttrTokenStream, Spac
 use rustc_ast::{self as ast};
 use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens};
 use rustc_errors::PResult;
-use rustc_span::{sym, Span};
+use rustc_session::parse::ParseSess;
+use rustc_span::{sym, Span, DUMMY_SP};
 
 use std::convert::TryInto;
 use std::ops::Range;
@@ -39,8 +40,13 @@ impl AttrWrapper {
     pub fn empty() -> AttrWrapper {
         AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX }
     }
-    // FIXME: Delay span bug here?
-    pub(crate) fn take_for_recovery(self) -> AttrVec {
+
+    pub(crate) fn take_for_recovery(self, sess: &ParseSess) -> AttrVec {
+        sess.span_diagnostic.delay_span_bug(
+            self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP),
+            "AttrVec is taken for recovery but no error is produced",
+        );
+
         self.attrs
     }
 
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 7355730c9eb..c9629ea49e0 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -7,35 +7,30 @@ use super::{
 };
 use crate::errors::{
     ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect,
-    BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
-    ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
-    DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr,
-    ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart,
-    FoundExprWouldBeStmt, HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
-    IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge,
+    BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric,
+    ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
+    ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet,
+    FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
+    IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub,
     InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
-    InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
-    InvalidInterpolatedExpression, InvalidLiteralSuffix, InvalidLiteralSuffixOnTupleIndex,
-    InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidNumLiteralBasePrefix,
-    InvalidNumLiteralSuffix, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
+    InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator,
+    InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
     LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
     MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
     MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
     NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
-    OctalFloatLiteralNotSupported, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
+    OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
     RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
     StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
     UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
 };
 use crate::maybe_recover_from_interpolated_ty_qpath;
-
 use core::mem;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::Spacing;
 use rustc_ast::util::case::Case;
 use rustc_ast::util::classify;
-use rustc_ast::util::literal::LitError;
 use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
 use rustc_ast::visit::Visitor;
 use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
@@ -47,7 +42,7 @@ use rustc_errors::{
     Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
     StashKey,
 };
-use rustc_session::errors::ExprParenthesesNeeded;
+use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_span::source_map::{self, Span, Spanned};
@@ -1415,9 +1410,9 @@ impl<'a> Parser<'a> {
 
     fn parse_lit_expr(&mut self) -> PResult<'a, P<Expr>> {
         let lo = self.token.span;
-        match self.parse_opt_lit() {
-            Some(literal) => {
-                let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal));
+        match self.parse_opt_token_lit() {
+            Some((token_lit, _)) => {
+                let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(token_lit));
                 self.maybe_recover_from_bad_qpath(expr)
             }
             None => self.try_macro_suggestion(),
@@ -1548,7 +1543,7 @@ impl<'a> Parser<'a> {
                 })
             });
             consume_colon = false;
-            Ok(self.mk_expr(lo, ExprKind::Lit(lit)))
+            Ok(self.mk_expr(lo, ExprKind::Lit(lit.token_lit)))
         } else if !ate_colon
             && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
         {
@@ -1625,9 +1620,9 @@ impl<'a> Parser<'a> {
 
     /// Emit an error when a char is parsed as a lifetime because of a missing quote
     pub(super) fn recover_unclosed_char(
-        &mut self,
+        &self,
         lifetime: Ident,
-        err: impl FnOnce(&mut Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
+        err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
     ) -> ast::Lit {
         if let Some(mut diag) =
             self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
@@ -1649,9 +1644,10 @@ impl<'a> Parser<'a> {
                 )
                 .emit();
         }
+        let name = lifetime.without_first_quote().name;
         ast::Lit {
-            token_lit: token::Lit::new(token::LitKind::Char, lifetime.name, None),
-            kind: ast::LitKind::Char(lifetime.name.as_str().chars().next().unwrap_or('_')),
+            token_lit: token::Lit::new(token::LitKind::Char, name, None),
+            kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')),
             span: lifetime.span,
         }
     }
@@ -1765,7 +1761,7 @@ impl<'a> Parser<'a> {
     /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
     /// and returns `None` if the next token is not literal at all.
     pub fn parse_str_lit(&mut self) -> Result<ast::StrLit, Option<Lit>> {
-        match self.parse_opt_lit() {
+        match self.parse_opt_ast_lit() {
             Some(lit) => match lit.kind {
                 ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit {
                     style,
@@ -1780,41 +1776,47 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> {
-        self.parse_opt_lit().ok_or(()).or_else(|()| {
-            if let token::Interpolated(inner) = &self.token.kind {
-                let expr = match inner.as_ref() {
-                    token::NtExpr(expr) => Some(expr),
-                    token::NtLiteral(expr) => Some(expr),
-                    _ => None,
-                };
-                if let Some(expr) = expr {
-                    if matches!(expr.kind, ExprKind::Err) {
-                        let mut err = InvalidInterpolatedExpression { span: self.token.span }
-                            .into_diagnostic(&self.sess.span_diagnostic);
-                        err.downgrade_to_delayed_bug();
-                        return Err(err);
-                    }
-                }
-            }
-            let token = self.token.clone();
-            let err = |self_: &mut Self| {
-                let msg = format!("unexpected token: {}", super::token_descr(&token));
-                self_.struct_span_err(token.span, &msg)
+    fn handle_missing_lit(&mut self) -> PResult<'a, Lit> {
+        if let token::Interpolated(inner) = &self.token.kind {
+            let expr = match inner.as_ref() {
+                token::NtExpr(expr) => Some(expr),
+                token::NtLiteral(expr) => Some(expr),
+                _ => None,
             };
-            // On an error path, eagerly consider a lifetime to be an unclosed character lit
-            if self.token.is_lifetime() {
-                let lt = self.expect_lifetime();
-                Ok(self.recover_unclosed_char(lt.ident, err))
-            } else {
-                Err(err(self))
+            if let Some(expr) = expr {
+                if matches!(expr.kind, ExprKind::Err) {
+                    let mut err = InvalidInterpolatedExpression { span: self.token.span }
+                        .into_diagnostic(&self.sess.span_diagnostic);
+                    err.downgrade_to_delayed_bug();
+                    return Err(err);
+                }
             }
-        })
+        }
+        let token = self.token.clone();
+        let err = |self_: &Self| {
+            let msg = format!("unexpected token: {}", super::token_descr(&token));
+            self_.struct_span_err(token.span, &msg)
+        };
+        // On an error path, eagerly consider a lifetime to be an unclosed character lit
+        if self.token.is_lifetime() {
+            let lt = self.expect_lifetime();
+            Ok(self.recover_unclosed_char(lt.ident, err))
+        } else {
+            Err(err(self))
+        }
     }
 
-    /// Matches `lit = true | false | token_lit`.
-    /// Returns `None` if the next token is not a literal.
-    pub(super) fn parse_opt_lit(&mut self) -> Option<Lit> {
+    pub(super) fn parse_token_lit(&mut self) -> PResult<'a, (token::Lit, Span)> {
+        self.parse_opt_token_lit()
+            .ok_or(())
+            .or_else(|()| self.handle_missing_lit().map(|lit| (lit.token_lit, lit.span)))
+    }
+
+    pub(super) fn parse_ast_lit(&mut self) -> PResult<'a, Lit> {
+        self.parse_opt_ast_lit().ok_or(()).or_else(|()| self.handle_missing_lit())
+    }
+
+    fn recover_after_dot(&mut self) -> Option<Token> {
         let mut recovered = None;
         if self.token == token::Dot {
             // Attempt to recover `.4` as `0.4`. We don't currently have any syntax where
@@ -1840,100 +1842,50 @@ impl<'a> Parser<'a> {
             }
         }
 
-        let token = recovered.as_ref().unwrap_or(&self.token);
-        match Lit::from_token(token) {
-            Ok(lit) => {
-                self.bump();
-                Some(lit)
-            }
-            Err(LitError::NotLiteral) => None,
-            Err(err) => {
-                let span = token.span;
-                let token::Literal(lit) = token.kind else {
-                    unreachable!();
-                };
-                self.bump();
-                self.report_lit_error(err, lit, span);
-                // Pack possible quotes and prefixes from the original literal into
-                // the error literal's symbol so they can be pretty-printed faithfully.
-                let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None);
-                let symbol = Symbol::intern(&suffixless_lit.to_string());
-                let lit = token::Lit::new(token::Err, symbol, lit.suffix);
-                Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!()))
-            }
-        }
+        recovered
     }
 
-    fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) {
-        // Checks if `s` looks like i32 or u1234 etc.
-        fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
-            s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit())
-        }
-
-        // Try to lowercase the prefix if it's a valid base prefix.
-        fn fix_base_capitalisation(s: &str) -> Option<String> {
-            if let Some(stripped) = s.strip_prefix('B') {
-                Some(format!("0b{stripped}"))
-            } else if let Some(stripped) = s.strip_prefix('O') {
-                Some(format!("0o{stripped}"))
-            } else if let Some(stripped) = s.strip_prefix('X') {
-                Some(format!("0x{stripped}"))
-            } else {
-                None
-            }
-        }
+    /// Matches `lit = true | false | token_lit`.
+    /// Returns `None` if the next token is not a literal.
+    pub(super) fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> {
+        let recovered = self.recover_after_dot();
+        let token = recovered.as_ref().unwrap_or(&self.token);
+        let span = token.span;
+        token::Lit::from_token(token).map(|token_lit| {
+            self.bump();
+            (token_lit, span)
+        })
+    }
 
-        let token::Lit { kind, suffix, .. } = lit;
-        match err {
-            // `NotLiteral` is not an error by itself, so we don't report
-            // it and give the parser opportunity to try something else.
-            LitError::NotLiteral => {}
-            // `LexerError` *is* an error, but it was already reported
-            // by lexer, so here we don't report it the second time.
-            LitError::LexerError => {}
-            LitError::InvalidSuffix => {
-                if let Some(suffix) = suffix {
-                    self.sess.emit_err(InvalidLiteralSuffix {
-                        span,
-                        kind: format!("{}", kind.descr()),
-                        suffix,
-                    });
-                }
-            }
-            LitError::InvalidIntSuffix => {
-                let suf = suffix.expect("suffix error with no suffix");
-                let suf = suf.as_str();
-                if looks_like_width_suffix(&['i', 'u'], &suf) {
-                    // If it looks like a width, try to be helpful.
-                    self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
-                } else if let Some(fixed) = fix_base_capitalisation(suf) {
-                    self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed });
-                } else {
-                    self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() });
-                }
-            }
-            LitError::InvalidFloatSuffix => {
-                let suf = suffix.expect("suffix error with no suffix");
-                let suf = suf.as_str();
-                if looks_like_width_suffix(&['f'], suf) {
-                    // If it looks like a width, try to be helpful.
-                    self.sess
-                        .emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() });
-                } else {
-                    self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() });
+    /// Matches `lit = true | false | token_lit`.
+    /// Returns `None` if the next token is not a literal.
+    pub(super) fn parse_opt_ast_lit(&mut self) -> Option<Lit> {
+        let recovered = self.recover_after_dot();
+        let token = recovered.as_ref().unwrap_or(&self.token);
+        match token::Lit::from_token(token) {
+            Some(token_lit) => {
+                match Lit::from_token_lit(token_lit, token.span) {
+                    Ok(lit) => {
+                        self.bump();
+                        Some(lit)
+                    }
+                    Err(err) => {
+                        let span = token.span;
+                        let token::Literal(lit) = token.kind else {
+                            unreachable!();
+                        };
+                        self.bump();
+                        report_lit_error(&self.sess, err, lit, span);
+                        // Pack possible quotes and prefixes from the original literal into
+                        // the error literal's symbol so they can be pretty-printed faithfully.
+                        let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None);
+                        let symbol = Symbol::intern(&suffixless_lit.to_string());
+                        let lit = token::Lit::new(token::Err, symbol, lit.suffix);
+                        Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!()))
+                    }
                 }
             }
-            LitError::NonDecimalFloat(base) => {
-                match base {
-                    16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }),
-                    8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }),
-                    2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }),
-                    _ => unreachable!(),
-                };
-            }
-            LitError::IntTooLarge => {
-                self.sess.emit_err(IntLiteralTooLarge { span });
-            }
+            None => None,
         }
     }
 
@@ -1958,8 +1910,8 @@ impl<'a> Parser<'a> {
 
         let lo = self.token.span;
         let minus_present = self.eat(&token::BinOp(token::Minus));
-        let lit = self.parse_lit()?;
-        let expr = self.mk_expr(lit.span, ExprKind::Lit(lit));
+        let (token_lit, span) = self.parse_token_lit()?;
+        let expr = self.mk_expr(span, ExprKind::Lit(token_lit));
 
         if minus_present {
             Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_unary(UnOp::Neg, expr)))
@@ -2272,7 +2224,7 @@ impl<'a> Parser<'a> {
                 self.mk_block_err(cond_span.shrink_to_hi())
             }
         } else {
-            let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
+            let attrs = self.parse_outer_attributes()?; // For recovery.
             let block = if self.check(&token::OpenDelim(Delimiter::Brace)) {
                 self.parse_block()?
             } else {
@@ -2289,7 +2241,7 @@ impl<'a> Parser<'a> {
                     })?
                 }
             };
-            self.error_on_if_block_attrs(lo, false, block.span, &attrs);
+            self.error_on_if_block_attrs(lo, false, block.span, attrs);
             block
         };
         let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None };
@@ -2350,7 +2302,7 @@ impl<'a> Parser<'a> {
     /// Parses an `else { ... }` expression (`else` token already eaten).
     fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
         let else_span = self.prev_token.span; // `else`
-        let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
+        let attrs = self.parse_outer_attributes()?; // For recovery.
         let expr = if self.eat_keyword(kw::If) {
             self.parse_if_expr()?
         } else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) {
@@ -2385,7 +2337,7 @@ impl<'a> Parser<'a> {
                 },
             }
         };
-        self.error_on_if_block_attrs(else_span, true, expr.span, &attrs);
+        self.error_on_if_block_attrs(else_span, true, expr.span, attrs);
         Ok(expr)
     }
 
@@ -2394,8 +2346,13 @@ impl<'a> Parser<'a> {
         ctx_span: Span,
         is_ctx_else: bool,
         branch_span: Span,
-        attrs: &[ast::Attribute],
+        attrs: AttrWrapper,
     ) {
+        if attrs.is_empty() {
+            return;
+        }
+
+        let attrs: &[ast::Attribute] = &attrs.take_for_recovery(self.sess);
         let (attributes, last) = match attrs {
             [] => return,
             [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 14dc490fb02..13a38a17735 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -503,8 +503,8 @@ impl<'a> Parser<'a> {
         parser
     }
 
-    pub fn forbid_recovery(mut self) -> Self {
-        self.recovery = Recovery::Forbidden;
+    pub fn recovery(mut self, recovery: Recovery) -> Self {
+        self.recovery = recovery;
         self
     }
 
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 52c11b4e35f..b3af37a5f70 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -420,7 +420,7 @@ impl<'a> Parser<'a> {
                 err.span_label(self_.token.span, format!("expected {}", expected));
                 err
             });
-            PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit)))
+            PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit.token_lit)))
         } else {
             // Try to parse everything else as literal with optional minus
             match self.parse_literal_maybe_minus() {
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 12753c6785c..7820bbc1789 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -10,8 +10,8 @@ use super::{
 use crate::errors::{
     AssignmentElseNotAllowed, CompoundAssignmentExpressionInLet, ConstLetMutuallyExclusive,
     DocCommentDoesNotDocumentAnything, ExpectedStatementAfterOuterAttr, InvalidCurlyInLetElse,
-    InvalidExpressionInLetElse, InvalidVariableDeclaration, InvalidVariableDeclarationSub,
-    WrapExpressionInParentheses,
+    InvalidExpressionInLetElse, InvalidIdentiferStartsWithNumber, InvalidVariableDeclaration,
+    InvalidVariableDeclarationSub, WrapExpressionInParentheses,
 };
 use crate::maybe_whole;
 
@@ -19,7 +19,7 @@ use rustc_ast as ast;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, TokenKind};
 use rustc_ast::util::classify;
-use rustc_ast::{AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
+use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle};
 use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Stmt};
 use rustc_ast::{StmtKind, DUMMY_NODE_ID};
 use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
@@ -101,7 +101,7 @@ impl<'a> Parser<'a> {
             self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
         } else if self.eat(&token::Semi) {
             // Do not attempt to parse an expression if we're done here.
-            self.error_outer_attrs(&attrs.take_for_recovery());
+            self.error_outer_attrs(attrs);
             self.mk_stmt(lo, StmtKind::Empty)
         } else if self.token != token::CloseDelim(Delimiter::Brace) {
             // Remainder are line-expr stmts.
@@ -120,7 +120,7 @@ impl<'a> Parser<'a> {
             }
             self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
         } else {
-            self.error_outer_attrs(&attrs.take_for_recovery());
+            self.error_outer_attrs(attrs);
             return Ok(None);
         }))
     }
@@ -199,8 +199,10 @@ impl<'a> Parser<'a> {
 
     /// Error on outer attributes in this context.
     /// Also error if the previous token was a doc comment.
-    fn error_outer_attrs(&self, attrs: &[Attribute]) {
-        if let [.., last] = attrs {
+    fn error_outer_attrs(&self, attrs: AttrWrapper) {
+        if !attrs.is_empty()
+        && let attrs = attrs.take_for_recovery(self.sess)
+        && let attrs @ [.., last] = &*attrs {
             if last.is_doc_comment() {
                 self.sess.emit_err(DocCommentDoesNotDocumentAnything {
                     span: last.span,
@@ -262,6 +264,7 @@ impl<'a> Parser<'a> {
             self.bump();
         }
 
+        self.report_invalid_identifier_error()?;
         let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?;
 
         let (err, ty) = if colon {
@@ -353,6 +356,17 @@ 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::Lit::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(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 4d78c5bd0e2..d6854f07025 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -1,5 +1,6 @@
 use super::{Parser, PathStyle, TokenType};
 
+use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg};
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 
 use rustc_ast::ptr::P;
@@ -270,14 +271,19 @@ impl<'a> Parser<'a> {
             TyKind::Infer
         } else if self.check_fn_front_matter(false, Case::Sensitive) {
             // Function pointer type
-            self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)?
+            self.parse_ty_bare_fn(lo, Vec::new(), None, recover_return_sign)?
         } else if self.check_keyword(kw::For) {
             // Function pointer type or bound list (trait object type) starting with a poly-trait.
             //   `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
             //   `for<'lt> Trait1<'lt> + Trait2 + 'a`
             let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
             if self.check_fn_front_matter(false, Case::Sensitive) {
-                self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)?
+                self.parse_ty_bare_fn(
+                    lo,
+                    lifetime_defs,
+                    Some(self.prev_token.span.shrink_to_lo()),
+                    recover_return_sign,
+                )?
             } else {
                 let path = self.parse_path(PathStyle::Type)?;
                 let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
@@ -519,7 +525,8 @@ impl<'a> Parser<'a> {
     fn parse_ty_bare_fn(
         &mut self,
         lo: Span,
-        params: Vec<GenericParam>,
+        mut params: Vec<GenericParam>,
+        param_insertion_point: Option<Span>,
         recover_return_sign: RecoverReturnSign,
     ) -> PResult<'a, TyKind> {
         let inherited_vis = rustc_ast::Visibility {
@@ -530,6 +537,9 @@ impl<'a> Parser<'a> {
         let span_start = self.token.span;
         let ast::FnHeader { ext, unsafety, constness, asyncness } =
             self.parse_fn_front_matter(&inherited_vis)?;
+        if self.may_recover() && self.token.kind == TokenKind::Lt {
+            self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
+        }
         let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
         let whole_span = lo.to(self.prev_token.span);
         if let ast::Const::Yes(span) = constness {
@@ -545,6 +555,48 @@ impl<'a> Parser<'a> {
         Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span })))
     }
 
+    /// Recover from function pointer types with a generic parameter list (e.g. `fn<'a>(&'a str)`).
+    fn recover_fn_ptr_with_generics(
+        &mut self,
+        lo: Span,
+        params: &mut Vec<GenericParam>,
+        param_insertion_point: Option<Span>,
+    ) -> PResult<'a, ()> {
+        let generics = self.parse_generics()?;
+        let arity = generics.params.len();
+
+        let mut lifetimes: Vec<_> = generics
+            .params
+            .into_iter()
+            .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime))
+            .collect();
+
+        let sugg = if !lifetimes.is_empty() {
+            let snippet =
+                lifetimes.iter().map(|param| param.ident.as_str()).intersperse(", ").collect();
+
+            let (left, snippet) = if let Some(span) = param_insertion_point {
+                (span, if params.is_empty() { snippet } else { format!(", {snippet}") })
+            } else {
+                (lo.shrink_to_lo(), format!("for<{snippet}> "))
+            };
+
+            Some(FnPtrWithGenericsSugg {
+                left,
+                snippet,
+                right: generics.span,
+                arity,
+                for_param_list_exists: param_insertion_point.is_some(),
+            })
+        } else {
+            None
+        };
+
+        self.sess.emit_err(FnPtrWithGenerics { span: generics.span, sugg });
+        params.append(&mut lifetimes);
+        Ok(())
+    }
+
     /// Emit an error for the given bad function pointer qualifier.
     fn error_fn_ptr_bad_qualifier(&self, span: Span, qual_span: Span, qual: &str) {
         self.struct_span_err(span, &format!("an `fn` pointer type cannot be `{}`", qual))
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 47477898b24..8e7f8bfe0f5 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -49,10 +49,12 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
                 MetaItemKind::List(nmis)
             }
             MacArgs::Eq(_, MacArgsEq::Ast(expr)) => {
-                if let ast::ExprKind::Lit(lit) = &expr.kind {
-                    if !lit.kind.is_unsuffixed() {
+                if let ast::ExprKind::Lit(token_lit) = expr.kind
+                    && let Ok(lit) = ast::Lit::from_token_lit(token_lit, expr.span)
+                {
+                    if token_lit.suffix.is_some() {
                         let mut err = sess.span_diagnostic.struct_span_err(
-                            lit.span,
+                            expr.span,
                             "suffixed literals are not allowed in attributes",
                         );
                         err.help(
@@ -61,7 +63,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
                         );
                         return Err(err);
                     } else {
-                        MetaItemKind::NameValue(lit.clone())
+                        MetaItemKind::NameValue(lit)
                     }
                 } else {
                     // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 54bf4d1d6b7..0113eb4e3d1 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -818,96 +818,94 @@ fn find_skips_from_snippet(
         _ => return (vec![], false),
     };
 
-    fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
-        let mut s = snippet.char_indices();
-        let mut skips = vec![];
-        while let Some((pos, c)) = s.next() {
-            match (c, s.clone().next()) {
-                // skip whitespace and empty lines ending in '\\'
-                ('\\', Some((next_pos, '\n'))) if !is_raw => {
-                    skips.push(pos);
-                    skips.push(next_pos);
-                    let _ = s.next();
+    if str_style.is_some() {
+        return (vec![], true);
+    }
 
-                    while let Some((pos, c)) = s.clone().next() {
-                        if matches!(c, ' ' | '\n' | '\t') {
-                            skips.push(pos);
-                            let _ = s.next();
-                        } else {
-                            break;
-                        }
-                    }
-                }
-                ('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => {
-                    skips.push(next_pos);
-                    let _ = s.next();
-                }
-                ('\\', Some((_, 'x'))) if !is_raw => {
-                    for _ in 0..3 {
-                        // consume `\xAB` literal
-                        if let Some((pos, _)) = s.next() {
-                            skips.push(pos);
-                        } else {
-                            break;
-                        }
+    let snippet = &snippet[1..snippet.len() - 1];
+
+    let mut s = snippet.char_indices();
+    let mut skips = vec![];
+    while let Some((pos, c)) = s.next() {
+        match (c, s.clone().next()) {
+            // skip whitespace and empty lines ending in '\\'
+            ('\\', Some((next_pos, '\n'))) => {
+                skips.push(pos);
+                skips.push(next_pos);
+                let _ = s.next();
+
+                while let Some((pos, c)) = s.clone().next() {
+                    if matches!(c, ' ' | '\n' | '\t') {
+                        skips.push(pos);
+                        let _ = s.next();
+                    } else {
+                        break;
                     }
                 }
-                ('\\', Some((_, 'u'))) if !is_raw => {
+            }
+            ('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => {
+                skips.push(next_pos);
+                let _ = s.next();
+            }
+            ('\\', Some((_, 'x'))) => {
+                for _ in 0..3 {
+                    // consume `\xAB` literal
                     if let Some((pos, _)) = s.next() {
                         skips.push(pos);
+                    } else {
+                        break;
                     }
-                    if let Some((next_pos, next_c)) = s.next() {
-                        if next_c == '{' {
-                            // consume up to 6 hexanumeric chars
-                            let digits_len =
-                                s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count();
-
-                            let len_utf8 = s
-                                .as_str()
-                                .get(..digits_len)
-                                .and_then(|digits| u32::from_str_radix(digits, 16).ok())
-                                .and_then(char::from_u32)
-                                .map_or(1, char::len_utf8);
-
-                            // Skip the digits, for chars that encode to more than 1 utf-8 byte
-                            // exclude as many digits as it is greater than 1 byte
-                            //
-                            // So for a 3 byte character, exclude 2 digits
-                            let required_skips =
-                                digits_len.saturating_sub(len_utf8.saturating_sub(1));
-
-                            // skip '{' and '}' also
-                            for pos in (next_pos..).take(required_skips + 2) {
-                                skips.push(pos)
-                            }
+                }
+            }
+            ('\\', Some((_, 'u'))) => {
+                if let Some((pos, _)) = s.next() {
+                    skips.push(pos);
+                }
+                if let Some((next_pos, next_c)) = s.next() {
+                    if next_c == '{' {
+                        // consume up to 6 hexanumeric chars
+                        let digits_len =
+                            s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count();
+
+                        let len_utf8 = s
+                            .as_str()
+                            .get(..digits_len)
+                            .and_then(|digits| u32::from_str_radix(digits, 16).ok())
+                            .and_then(char::from_u32)
+                            .map_or(1, char::len_utf8);
+
+                        // Skip the digits, for chars that encode to more than 1 utf-8 byte
+                        // exclude as many digits as it is greater than 1 byte
+                        //
+                        // So for a 3 byte character, exclude 2 digits
+                        let required_skips = digits_len.saturating_sub(len_utf8.saturating_sub(1));
+
+                        // skip '{' and '}' also
+                        for pos in (next_pos..).take(required_skips + 2) {
+                            skips.push(pos)
+                        }
 
-                            s.nth(digits_len);
-                        } else if next_c.is_digit(16) {
-                            skips.push(next_pos);
-                            // We suggest adding `{` and `}` when appropriate, accept it here as if
-                            // it were correct
-                            let mut i = 0; // consume up to 6 hexanumeric chars
-                            while let (Some((next_pos, c)), _) = (s.next(), i < 6) {
-                                if c.is_digit(16) {
-                                    skips.push(next_pos);
-                                } else {
-                                    break;
-                                }
-                                i += 1;
+                        s.nth(digits_len);
+                    } else if next_c.is_digit(16) {
+                        skips.push(next_pos);
+                        // We suggest adding `{` and `}` when appropriate, accept it here as if
+                        // it were correct
+                        let mut i = 0; // consume up to 6 hexanumeric chars
+                        while let (Some((next_pos, c)), _) = (s.next(), i < 6) {
+                            if c.is_digit(16) {
+                                skips.push(next_pos);
+                            } else {
+                                break;
                             }
+                            i += 1;
                         }
                     }
                 }
-                _ => {}
             }
+            _ => {}
         }
-        skips
     }
-
-    let r_start = str_style.map_or(0, |r| r + 1);
-    let r_end = str_style.unwrap_or(0);
-    let s = &snippet[r_start + 1..snippet.len() - r_end - 1];
-    (find_skips(s, str_style.is_some()), true)
+    (skips, true)
 }
 
 #[cfg(test)]
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 2b6ff0a5cb9..6b8cd071373 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -219,18 +219,6 @@ impl CheckAttrVisitor<'_> {
             return;
         }
 
-        // FIXME(@lcnr): this doesn't belong here.
-        if matches!(
-            target,
-            Target::Closure
-                | Target::Fn
-                | Target::Method(_)
-                | Target::ForeignFn
-                | Target::ForeignStatic
-        ) {
-            self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
-        }
-
         self.check_repr(attrs, span, target, item, hir_id);
         self.check_used(attrs, target);
     }
@@ -423,8 +411,7 @@ impl CheckAttrVisitor<'_> {
         if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) {
             for p in generics.params {
                 let hir::GenericParamKind::Type { .. } = p.kind else { continue };
-                let param_id = tcx.hir().local_def_id(p.hir_id);
-                let default = tcx.object_lifetime_default(param_id);
+                let default = tcx.object_lifetime_default(p.def_id);
                 let repr = match default {
                     ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
                     ObjectLifetimeDefault::Static => "'static".to_owned(),
@@ -2150,7 +2137,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
     }
 
     fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {
-        self.check_attributes(variant.id, variant.span, Target::Variant, None);
+        self.check_attributes(variant.hir_id, variant.span, Target::Variant, None);
         intravisit::walk_variant(self, variant)
     }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 21b487d8ca1..d4722234a8f 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -362,7 +362,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
         let has_repr_c = self.repr_has_repr_c;
         let has_repr_simd = self.repr_has_repr_simd;
         let live_fields = def.fields().iter().filter_map(|f| {
-            let def_id = tcx.hir().local_def_id(f.hir_id);
+            let def_id = f.def_id;
             if has_repr_c || (f.is_positional() && has_repr_simd) {
                 return Some(def_id);
             }
@@ -522,17 +522,13 @@ fn check_item<'tcx>(
         DefKind::Enum => {
             let item = tcx.hir().item(id);
             if let hir::ItemKind::Enum(ref enum_def, _) = item.kind {
-                let hir = tcx.hir();
                 if allow_dead_code {
-                    worklist.extend(
-                        enum_def.variants.iter().map(|variant| hir.local_def_id(variant.id)),
-                    );
+                    worklist.extend(enum_def.variants.iter().map(|variant| variant.def_id));
                 }
 
                 for variant in enum_def.variants {
-                    if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
-                        struct_constructors
-                            .insert(hir.local_def_id(ctor_hir_id), hir.local_def_id(variant.id));
+                    if let Some(ctor_def_id) = variant.data.ctor_def_id() {
+                        struct_constructors.insert(ctor_def_id, variant.def_id);
                     }
                 }
             }
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 188efc528ef..99efed0b7fb 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -219,7 +219,7 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
             let item = tcx.hir().item(id);
             if let hir::ItemKind::Enum(def, ..) = &item.kind {
                 for variant in def.variants {
-                    collector.check_for_lang(Target::Variant, variant.id);
+                    collector.check_for_lang(Target::Variant, variant.hir_id);
                 }
             }
         }
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index c181de48a9a..0100860afb9 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -413,7 +413,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
                 }
                 intravisit::walk_expr(self, expr);
             }
-            hir::ExprKind::Closure { .. } => {
+            hir::ExprKind::Closure(closure) => {
                 // Interesting control flow (for loops can contain labeled
                 // breaks or continues)
                 self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
@@ -423,8 +423,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
                 // in better error messages than just pointing at the closure
                 // construction site.
                 let mut call_caps = Vec::new();
-                let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id);
-                if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
+                if let Some(upvars) = self.tcx.upvars_mentioned(closure.def_id) {
                     call_caps.extend(upvars.keys().map(|var_id| {
                         let upvar = upvars[var_id];
                         let upvar_ln = self.add_live_node(UpvarNode(upvar.span));
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 78afa2f25f8..88bd655d8d3 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -358,9 +358,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
                 const_stab_inherit = InheritConstStability::Yes;
             }
             hir::ItemKind::Struct(ref sd, _) => {
-                if let Some(ctor_hir_id) = sd.ctor_hir_id() {
+                if let Some(ctor_def_id) = sd.ctor_def_id() {
                     self.annotate(
-                        self.tcx.hir().local_def_id(ctor_hir_id),
+                        ctor_def_id,
                         i.span,
                         None,
                         AnnotationKind::Required,
@@ -435,7 +435,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
 
     fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
         self.annotate(
-            self.tcx.hir().local_def_id(var.id),
+            var.def_id,
             var.span,
             None,
             AnnotationKind::Required,
@@ -443,9 +443,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
             InheritConstStability::No,
             InheritStability::Yes,
             |v| {
-                if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
+                if let Some(ctor_def_id) = var.data.ctor_def_id() {
                     v.annotate(
-                        v.tcx.hir().local_def_id(ctor_hir_id),
+                        ctor_def_id,
                         var.span,
                         None,
                         AnnotationKind::Required,
@@ -463,7 +463,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
 
     fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
         self.annotate(
-            self.tcx.hir().local_def_id(s.hir_id),
+            s.def_id,
             s.span,
             None,
             AnnotationKind::Required,
@@ -500,7 +500,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
         };
 
         self.annotate(
-            self.tcx.hir().local_def_id(p.hir_id),
+            p.def_id,
             p.span,
             None,
             kind,
@@ -536,6 +536,14 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
             return;
         }
 
+        // if the const impl is derived using the `derive_const` attribute,
+        // then it would be "stable" at least for the impl.
+        // We gate usages of it using `feature(const_trait_impl)` anyways
+        // so there is no unstable leakage
+        if self.tcx.is_builtin_derive(def_id.to_def_id()) {
+            return;
+        }
+
         let is_const = self.tcx.is_const_fn(def_id.to_def_id())
             || self.tcx.is_const_trait_impl_raw(def_id.to_def_id());
         let is_stable = self
@@ -593,15 +601,15 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> {
     }
 
     fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) {
-        self.check_missing_stability(self.tcx.hir().local_def_id(var.id), var.span);
-        if let Some(ctor_hir_id) = var.data.ctor_hir_id() {
-            self.check_missing_stability(self.tcx.hir().local_def_id(ctor_hir_id), var.span);
+        self.check_missing_stability(var.def_id, var.span);
+        if let Some(ctor_def_id) = var.data.ctor_def_id() {
+            self.check_missing_stability(ctor_def_id, var.span);
         }
         intravisit::walk_variant(self, var);
     }
 
     fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) {
-        self.check_missing_stability(self.tcx.hir().local_def_id(s.hir_id), s.span);
+        self.check_missing_stability(s.def_id, s.span);
         intravisit::walk_field_def(self, s);
     }
 
diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs
index 68d9bf22bf9..9e41efce9ce 100644
--- a/compiler/rustc_passes/src/upvars.rs
+++ b/compiler/rustc_passes/src/upvars.rs
@@ -75,9 +75,8 @@ impl<'tcx> Visitor<'tcx> for CaptureCollector<'_, 'tcx> {
     }
 
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
-        if let hir::ExprKind::Closure { .. } = expr.kind {
-            let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id);
-            if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
+        if let hir::ExprKind::Closure(closure) = expr.kind {
+            if let Some(upvars) = self.tcx.upvars_mentioned(closure.def_id) {
                 // Every capture of a closure expression is a local in scope,
                 // that is moved/copied/borrowed into the closure value, and
                 // for this analysis they are like any other access to a local.
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index e17f85c1aae..1d9ae539b60 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -419,11 +419,6 @@ impl<'tcx> EmbargoVisitor<'tcx> {
         self.effective_visibilities.public_at_level(def_id)
     }
 
-    fn update_with_hir_id(&mut self, hir_id: hir::HirId, level: Option<Level>) -> Option<Level> {
-        let def_id = self.tcx.hir().local_def_id(hir_id);
-        self.update(def_id, level)
-    }
-
     /// Updates node level and returns the updated level.
     fn update(&mut self, def_id: LocalDefId, level: Option<Level>) -> Option<Level> {
         let old_level = self.get(def_id);
@@ -573,10 +568,9 @@ impl<'tcx> EmbargoVisitor<'tcx> {
                     | hir::ItemKind::Union(ref struct_def, _) = item.kind
                     {
                         for field in struct_def.fields() {
-                            let def_id = self.tcx.hir().local_def_id(field.hir_id);
-                            let field_vis = self.tcx.local_visibility(def_id);
+                            let field_vis = self.tcx.local_visibility(field.def_id);
                             if field_vis.is_accessible_from(module, self.tcx) {
-                                self.reach(def_id, level).ty();
+                                self.reach(field.def_id, level).ty();
                             }
                         }
                     } else {
@@ -641,12 +635,12 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
         match item.kind {
             hir::ItemKind::Enum(ref def, _) => {
                 for variant in def.variants {
-                    let variant_level = self.update_with_hir_id(variant.id, item_level);
-                    if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
-                        self.update_with_hir_id(ctor_hir_id, item_level);
+                    let variant_level = self.update(variant.def_id, item_level);
+                    if let Some(ctor_def_id) = variant.data.ctor_def_id() {
+                        self.update(ctor_def_id, item_level);
                     }
                     for field in variant.data.fields() {
-                        self.update_with_hir_id(field.hir_id, variant_level);
+                        self.update(field.def_id, variant_level);
                     }
                 }
             }
@@ -665,14 +659,13 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
                 }
             }
             hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
-                if let Some(ctor_hir_id) = def.ctor_hir_id() {
-                    self.update_with_hir_id(ctor_hir_id, item_level);
+                if let Some(ctor_def_id) = def.ctor_def_id() {
+                    self.update(ctor_def_id, item_level);
                 }
                 for field in def.fields() {
-                    let def_id = self.tcx.hir().local_def_id(field.hir_id);
-                    let vis = self.tcx.visibility(def_id);
+                    let vis = self.tcx.visibility(field.def_id);
                     if vis.is_public() {
-                        self.update_with_hir_id(field.hir_id, item_level);
+                        self.update(field.def_id, item_level);
                     }
                 }
             }
@@ -782,18 +775,16 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
                     self.reach(item.owner_id.def_id, item_level).generics().predicates();
                 }
                 for variant in def.variants {
-                    let variant_level = self.get(self.tcx.hir().local_def_id(variant.id));
+                    let variant_level = self.get(variant.def_id);
                     if variant_level.is_some() {
                         for field in variant.data.fields() {
-                            self.reach(self.tcx.hir().local_def_id(field.hir_id), variant_level)
-                                .ty();
+                            self.reach(field.def_id, variant_level).ty();
                         }
                         // Corner case: if the variant is reachable, but its
                         // enum is not, make the enum reachable as well.
                         self.reach(item.owner_id.def_id, variant_level).ty();
                     }
-                    if let Some(hir_id) = variant.data.ctor_hir_id() {
-                        let ctor_def_id = self.tcx.hir().local_def_id(hir_id);
+                    if let Some(ctor_def_id) = variant.data.ctor_def_id() {
                         let ctor_level = self.get(ctor_def_id);
                         if ctor_level.is_some() {
                             self.reach(item.owner_id.def_id, ctor_level).ty();
@@ -818,15 +809,13 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
                 if item_level.is_some() {
                     self.reach(item.owner_id.def_id, item_level).generics().predicates();
                     for field in struct_def.fields() {
-                        let def_id = self.tcx.hir().local_def_id(field.hir_id);
-                        let field_level = self.get(def_id);
+                        let field_level = self.get(field.def_id);
                         if field_level.is_some() {
-                            self.reach(def_id, field_level).ty();
+                            self.reach(field.def_id, field_level).ty();
                         }
                     }
                 }
-                if let Some(hir_id) = struct_def.ctor_hir_id() {
-                    let ctor_def_id = self.tcx.hir().local_def_id(hir_id);
+                if let Some(ctor_def_id) = struct_def.ctor_def_id() {
                     let ctor_level = self.get(ctor_def_id);
                     if ctor_level.is_some() {
                         self.reach(item.owner_id.def_id, ctor_level).ty();
@@ -957,26 +946,21 @@ impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
         match item.kind {
             hir::ItemKind::Enum(ref def, _) => {
                 for variant in def.variants.iter() {
-                    let variant_id = self.tcx.hir().local_def_id(variant.id);
-                    self.effective_visibility_diagnostic(variant_id);
-                    if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
-                        let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
+                    self.effective_visibility_diagnostic(variant.def_id);
+                    if let Some(ctor_def_id) = variant.data.ctor_def_id() {
                         self.effective_visibility_diagnostic(ctor_def_id);
                     }
                     for field in variant.data.fields() {
-                        let def_id = self.tcx.hir().local_def_id(field.hir_id);
-                        self.effective_visibility_diagnostic(def_id);
+                        self.effective_visibility_diagnostic(field.def_id);
                     }
                 }
             }
             hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
-                if let Some(ctor_hir_id) = def.ctor_hir_id() {
-                    let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
+                if let Some(ctor_def_id) = def.ctor_def_id() {
                     self.effective_visibility_diagnostic(ctor_def_id);
                 }
                 for field in def.fields() {
-                    let def_id = self.tcx.hir().local_def_id(field.hir_id);
-                    self.effective_visibility_diagnostic(def_id);
+                    self.effective_visibility_diagnostic(field.def_id);
                 }
             }
             _ => {}
@@ -1719,7 +1703,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
     }
 
     fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
-        if self.effective_visibilities.is_reachable(self.tcx.hir().local_def_id(v.id)) {
+        if self.effective_visibilities.is_reachable(v.def_id) {
             self.in_variant = true;
             intravisit::walk_variant(self, v);
             self.in_variant = false;
@@ -1727,8 +1711,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
     }
 
     fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
-        let def_id = self.tcx.hir().local_def_id(s.hir_id);
-        let vis = self.tcx.visibility(def_id);
+        let vis = self.tcx.visibility(s.def_id);
         if vis.is_public() || self.in_variant {
             intravisit::walk_field_def(self, s);
         }
@@ -1982,8 +1965,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
 
                     for variant in def.variants {
                         for field in variant.data.fields() {
-                            self.check(self.tcx.hir().local_def_id(field.hir_id), item_visibility)
-                                .ty();
+                            self.check(field.def_id, item_visibility).ty();
                         }
                     }
                 }
@@ -2010,9 +1992,8 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
                     self.check(item.owner_id.def_id, item_visibility).generics().predicates();
 
                     for field in struct_def.fields() {
-                        let def_id = tcx.hir().local_def_id(field.hir_id);
-                        let field_visibility = tcx.local_visibility(def_id);
-                        self.check(def_id, min(item_visibility, field_visibility, tcx)).ty();
+                        let field_visibility = tcx.local_visibility(field.def_id);
+                        self.check(field.def_id, min(item_visibility, field_visibility, tcx)).ty();
                     }
                 }
             }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 423c5727533..e7e419c9b42 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -469,9 +469,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                         }
 
                         // Replace `use foo::{ self };` with `use foo;`
+                        let self_span = source.ident.span;
                         source = module_path.pop().unwrap();
                         if rename.is_none() {
-                            ident = source.ident;
+                            // Keep the span of `self`, but the name of `foo`
+                            ident = Ident { name: source.ident.name, span: self_span };
                         }
                     }
                 } else {
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index fa6d34be0cc..82dcc7efb1b 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -72,7 +72,7 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
                 update(node_id);
                 if let ImportKind::Single { additional_ids: (id1, id2), .. } = import.kind {
                     // In theory all the single import IDs have individual visibilities and
-                    // effective visibilities, but in practice these IDs go straigth to HIR
+                    // effective visibilities, but in practice these IDs go straight to HIR
                     // where all their few uses assume that their (effective) visibility
                     // applies to the whole syntactic `use` item. So they all get the same
                     // value which is the maximum of all bindings. Maybe HIR for imports
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index a1ff477c6fe..9ca3588fff4 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1932,6 +1932,11 @@ impl<'a> Resolver<'a> {
         }
     }
 
+    /// For rustdoc.
+    pub fn get_partial_res(&self, node_id: NodeId) -> Option<PartialRes> {
+        self.partial_res_map.get(&node_id).copied()
+    }
+
     /// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
     #[inline]
     pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index df5d992f663..0e579379ec8 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -527,9 +527,9 @@ impl<'tcx> DumpVisitor<'tcx> {
                     let value = format!("{}::{} {{ {} }}", enum_data.name, name, fields_str);
                     if !self.span.filter_generated(name_span) {
                         let span = self.span_from_span(name_span);
-                        let id = id_from_hir_id(variant.id, &self.save_ctxt);
+                        let id = id_from_hir_id(variant.hir_id, &self.save_ctxt);
                         let parent = Some(id_from_def_id(item.owner_id.to_def_id()));
-                        let attrs = self.tcx.hir().attrs(variant.id);
+                        let attrs = self.tcx.hir().attrs(variant.hir_id);
 
                         self.dumper.dump_def(
                             &access,
@@ -552,7 +552,7 @@ impl<'tcx> DumpVisitor<'tcx> {
                 }
                 ref v => {
                     let mut value = format!("{}::{}", enum_data.name, name);
-                    if let hir::VariantData::Tuple(fields, _) = v {
+                    if let hir::VariantData::Tuple(fields, _, _) = v {
                         value.push('(');
                         value.push_str(
                             &fields
@@ -565,9 +565,9 @@ impl<'tcx> DumpVisitor<'tcx> {
                     }
                     if !self.span.filter_generated(name_span) {
                         let span = self.span_from_span(name_span);
-                        let id = id_from_hir_id(variant.id, &self.save_ctxt);
+                        let id = id_from_hir_id(variant.hir_id, &self.save_ctxt);
                         let parent = Some(id_from_def_id(item.owner_id.to_def_id()));
-                        let attrs = self.tcx.hir().attrs(variant.id);
+                        let attrs = self.tcx.hir().attrs(variant.hir_id);
 
                         self.dumper.dump_def(
                             &access,
@@ -591,7 +591,7 @@ impl<'tcx> DumpVisitor<'tcx> {
             }
 
             for field in variant.data.fields() {
-                self.process_struct_field_def(field, variant.id);
+                self.process_struct_field_def(field, variant.hir_id);
                 self.visit_ty(field.ty);
             }
         }
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index d0155c908a2..ffe8edf69b7 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -319,7 +319,7 @@ impl<'tcx> SaveContext<'tcx> {
                     qualname,
                     value,
                     parent: None,
-                    children: def.variants.iter().map(|v| id_from_hir_id(v.id, self)).collect(),
+                    children: def.variants.iter().map(|v| id_from_hir_id(v.hir_id, self)).collect(),
                     decl_id: None,
                     docs: self.docs_for_attrs(attrs),
                     sig: sig::item_signature(item, self),
diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs
index 83c51d213be..9fcba3e46f1 100644
--- a/compiler/rustc_save_analysis/src/sig.rs
+++ b/compiler/rustc_save_analysis/src/sig.rs
@@ -693,7 +693,7 @@ impl<'hir> Sig for hir::Variant<'hir> {
                 text.push('}');
                 Ok(Signature { text, defs, refs })
             }
-            hir::VariantData::Tuple(fields, id) => {
+            hir::VariantData::Tuple(fields, id, _) => {
                 let name_def = SigElement {
                     id: id_from_hir_id(id, scx),
                     start: offset,
@@ -712,7 +712,7 @@ impl<'hir> Sig for hir::Variant<'hir> {
                 text.push(')');
                 Ok(Signature { text, defs, refs })
             }
-            hir::VariantData::Unit(id) => {
+            hir::VariantData::Unit(id, _) => {
                 let name_def = SigElement {
                     id: id_from_hir_id(id, scx),
                     start: offset,
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index bf542faec41..4bfa583fc72 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -1,6 +1,9 @@
 use std::num::NonZeroU32;
 
 use crate::cgu_reuse_tracker::CguReuse;
+use crate::parse::ParseSess;
+use rustc_ast::token;
+use rustc_ast::util::literal::LitError;
 use rustc_errors::MultiSpan;
 use rustc_macros::Diagnostic;
 use rustc_span::{Span, Symbol};
@@ -191,3 +194,162 @@ pub enum UnleashedFeatureHelp {
         span: Span,
     },
 }
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_literal_suffix)]
+pub(crate) struct InvalidLiteralSuffix {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    // FIXME(#100717)
+    pub kind: String,
+    pub suffix: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_int_literal_width)]
+#[help]
+pub(crate) struct InvalidIntLiteralWidth {
+    #[primary_span]
+    pub span: Span,
+    pub width: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_num_literal_base_prefix)]
+#[note]
+pub(crate) struct InvalidNumLiteralBasePrefix {
+    #[primary_span]
+    #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")]
+    pub span: Span,
+    pub fixed: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_num_literal_suffix)]
+#[help]
+pub(crate) struct InvalidNumLiteralSuffix {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub suffix: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_float_literal_width)]
+#[help]
+pub(crate) struct InvalidFloatLiteralWidth {
+    #[primary_span]
+    pub span: Span,
+    pub width: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_invalid_float_literal_suffix)]
+#[help]
+pub(crate) struct InvalidFloatLiteralSuffix {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub suffix: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_int_literal_too_large)]
+pub(crate) struct IntLiteralTooLarge {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_hexadecimal_float_literal_not_supported)]
+pub(crate) struct HexadecimalFloatLiteralNotSupported {
+    #[primary_span]
+    #[label(parser_not_supported)]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_octal_float_literal_not_supported)]
+pub(crate) struct OctalFloatLiteralNotSupported {
+    #[primary_span]
+    #[label(parser_not_supported)]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parser_binary_float_literal_not_supported)]
+pub(crate) struct BinaryFloatLiteralNotSupported {
+    #[primary_span]
+    #[label(parser_not_supported)]
+    pub span: Span,
+}
+
+pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: Span) {
+    // Checks if `s` looks like i32 or u1234 etc.
+    fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
+        s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit())
+    }
+
+    // Try to lowercase the prefix if it's a valid base prefix.
+    fn fix_base_capitalisation(s: &str) -> Option<String> {
+        if let Some(stripped) = s.strip_prefix('B') {
+            Some(format!("0b{stripped}"))
+        } else if let Some(stripped) = s.strip_prefix('O') {
+            Some(format!("0o{stripped}"))
+        } else if let Some(stripped) = s.strip_prefix('X') {
+            Some(format!("0x{stripped}"))
+        } else {
+            None
+        }
+    }
+
+    let token::Lit { kind, suffix, .. } = lit;
+    match err {
+        // `LexerError` is an error, but it was already reported
+        // by lexer, so here we don't report it the second time.
+        LitError::LexerError => {}
+        LitError::InvalidSuffix => {
+            if let Some(suffix) = suffix {
+                sess.emit_err(InvalidLiteralSuffix {
+                    span,
+                    kind: format!("{}", kind.descr()),
+                    suffix,
+                });
+            }
+        }
+        LitError::InvalidIntSuffix => {
+            let suf = suffix.expect("suffix error with no suffix");
+            let suf = suf.as_str();
+            if looks_like_width_suffix(&['i', 'u'], &suf) {
+                // If it looks like a width, try to be helpful.
+                sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
+            } else if let Some(fixed) = fix_base_capitalisation(suf) {
+                sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed });
+            } else {
+                sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() });
+            }
+        }
+        LitError::InvalidFloatSuffix => {
+            let suf = suffix.expect("suffix error with no suffix");
+            let suf = suf.as_str();
+            if looks_like_width_suffix(&['f'], suf) {
+                // If it looks like a width, try to be helpful.
+                sess.emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() });
+            } else {
+                sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() });
+            }
+        }
+        LitError::NonDecimalFloat(base) => {
+            match base {
+                16 => sess.emit_err(HexadecimalFloatLiteralNotSupported { span }),
+                8 => sess.emit_err(OctalFloatLiteralNotSupported { span }),
+                2 => sess.emit_err(BinaryFloatLiteralNotSupported { span }),
+                _ => unreachable!(),
+            };
+        }
+        LitError::IntTooLarge => {
+            sess.emit_err(IntLiteralTooLarge { span });
+        }
+    }
+}
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index b48db73618b..a56450a3573 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -170,8 +170,6 @@ symbols! {
         Count,
         Cow,
         Debug,
-        DebugStruct,
-        DebugTuple,
         Decodable,
         Decoder,
         DecorateLint,
@@ -190,9 +188,6 @@ symbols! {
         Error,
         File,
         FileType,
-        Fn,
-        FnMut,
-        FnOnce,
         FormatSpec,
         Formatter,
         From,
@@ -211,7 +206,6 @@ symbols! {
         Input,
         Into,
         IntoDiagnostic,
-        IntoFuture,
         IntoIterator,
         IoRead,
         IoWrite,
@@ -256,7 +250,6 @@ symbols! {
         Pointer,
         Poll,
         ProcMacro,
-        ProcMacroHack,
         ProceduralMasqueradeDummyType,
         Range,
         RangeFrom,
@@ -332,7 +325,6 @@ symbols! {
         abi_vectorcall,
         abi_x86_interrupt,
         abort,
-        aborts,
         add,
         add_assign,
         add_with_overflow,
@@ -344,7 +336,6 @@ symbols! {
         align,
         align_offset,
         alignment,
-        alignstack,
         all,
         alloc,
         alloc_error_handler,
@@ -433,7 +424,6 @@ symbols! {
         bool,
         borrowck_graphviz_format,
         borrowck_graphviz_postflow,
-        borrowck_graphviz_preflow,
         box_free,
         box_patterns,
         box_syntax,
@@ -462,7 +452,6 @@ symbols! {
         cfg_doctest,
         cfg_eval,
         cfg_hide,
-        cfg_macro,
         cfg_panic,
         cfg_sanitize,
         cfg_target_abi,
@@ -470,7 +459,6 @@ symbols! {
         cfg_target_feature,
         cfg_target_has_atomic,
         cfg_target_has_atomic_equal_alignment,
-        cfg_target_has_atomic_load_store,
         cfg_target_thread_local,
         cfg_target_vendor,
         cfg_version,
@@ -495,19 +483,15 @@ symbols! {
         cold,
         collapse_debuginfo,
         column,
-        column_macro,
-        compare_and_swap,
         compare_exchange,
         compare_exchange_weak,
         compile_error,
-        compile_error_macro,
         compiler,
         compiler_builtins,
         compiler_fence,
         concat,
         concat_bytes,
         concat_idents,
-        concat_macro,
         conservative_impl_trait,
         console,
         const_allocate,
@@ -528,7 +512,6 @@ symbols! {
         const_fn_unsize,
         const_for,
         const_format_args,
-        const_generic_defaults,
         const_generics,
         const_generics_defaults,
         const_if_match,
@@ -547,22 +530,19 @@ symbols! {
         const_trait,
         const_trait_bound_opt_out,
         const_trait_impl,
-        const_transmute,
         const_try,
         constant,
         constructor,
-        contents,
         context,
-        convert,
         copy,
         copy_closures,
         copy_nonoverlapping,
         copysignf32,
         copysignf64,
         core,
-        core_intrinsics,
         core_panic,
         core_panic_2015_macro,
+        core_panic_2021_macro,
         core_panic_macro,
         cosf32,
         cosf64,
@@ -598,7 +578,6 @@ symbols! {
         debug_assertions,
         debug_struct,
         debug_struct_fields_finish,
-        debug_trait_builder,
         debug_tuple,
         debug_tuple_fields_finish,
         debugger_visualizer,
@@ -630,7 +609,6 @@ symbols! {
         discriminant_type,
         discriminant_value,
         dispatch_from_dyn,
-        display_trait,
         div,
         div_assign,
         doc,
@@ -661,7 +639,6 @@ symbols! {
         dyn_star,
         dyn_trait,
         e,
-        edition_macro_pats,
         edition_panic,
         eh_catch_typeinfo,
         eh_personality,
@@ -674,7 +651,6 @@ symbols! {
         encode,
         end,
         env,
-        env_macro,
         eprint_macro,
         eprintln_macro,
         eq,
@@ -724,9 +700,7 @@ symbols! {
         field,
         field_init_shorthand,
         file,
-        file_macro,
         fill,
-        finish,
         flags,
         float,
         float_to_int_unchecked,
@@ -735,8 +709,6 @@ symbols! {
         fmaf32,
         fmaf64,
         fmt,
-        fmt_as_str,
-        fmt_internals,
         fmul_fast,
         fn_align,
         fn_must_use,
@@ -751,7 +723,6 @@ symbols! {
         format_args_macro,
         format_args_nl,
         format_macro,
-        fp,
         freeze,
         freg,
         frem_fast,
@@ -814,7 +785,6 @@ symbols! {
         ignore,
         impl_header_lifetime_elision,
         impl_lint_pass,
-        impl_macros,
         impl_trait_in_bindings,
         impl_trait_in_fn_trait_return,
         implied_by,
@@ -826,7 +796,6 @@ symbols! {
         include,
         include_bytes,
         include_bytes_macro,
-        include_macro,
         include_str,
         include_str_macro,
         inclusive_range_syntax,
@@ -844,7 +813,6 @@ symbols! {
         instruction_set,
         integer_: "integer",
         integral,
-        intel,
         into_future,
         into_iter,
         intra_doc_pointers,
@@ -881,7 +849,6 @@ symbols! {
         lifetimes,
         likely,
         line,
-        line_macro,
         link,
         link_args,
         link_cfg,
@@ -926,7 +893,6 @@ symbols! {
         masked,
         match_beginning_vert,
         match_default_bindings,
-        matches_macro,
         maxnumf32,
         maxnumf64,
         may_dangle,
@@ -965,7 +931,6 @@ symbols! {
         modifiers,
         module,
         module_path,
-        module_path_macro,
         more_qualified_paths,
         more_struct_aliases,
         movbe_target_feature,
@@ -1035,7 +1000,6 @@ symbols! {
         non_exhaustive,
         non_exhaustive_omitted_patterns_lint,
         non_modrs_mods,
-        none_error,
         nontemporal_store,
         noop_method_borrow,
         noop_method_clone,
@@ -1060,7 +1024,6 @@ symbols! {
         optin_builtin_traits,
         option,
         option_env,
-        option_env_macro,
         options,
         or,
         or_patterns,
@@ -1103,7 +1066,6 @@ symbols! {
         plugins,
         pointee_trait,
         pointer,
-        pointer_trait_fmt,
         poll,
         position,
         post_dash_lto: "post-lto",
@@ -1130,7 +1092,6 @@ symbols! {
         proc_dash_macro: "proc-macro",
         proc_macro,
         proc_macro_attribute,
-        proc_macro_def_site,
         proc_macro_derive,
         proc_macro_expr,
         proc_macro_gen,
@@ -1231,9 +1192,6 @@ symbols! {
         rust_cold_cc,
         rust_eh_catch_typeinfo,
         rust_eh_personality,
-        rust_eh_register_frames,
-        rust_eh_unregister_frames,
-        rust_oom,
         rustc,
         rustc_allocator,
         rustc_allocator_zeroed,
@@ -1252,6 +1210,7 @@ symbols! {
         rustc_deallocator,
         rustc_def_path,
         rustc_default_body_unstable,
+        rustc_deny_explicit_impl,
         rustc_diagnostic_item,
         rustc_diagnostic_macros,
         rustc_dirty,
@@ -1306,7 +1265,6 @@ symbols! {
         rustc_serialize,
         rustc_skip_array_during_method_dispatch,
         rustc_specialization_trait,
-        rustc_stable,
         rustc_std_internal_symbol,
         rustc_strict_coherence,
         rustc_symbol_name,
@@ -1434,7 +1392,6 @@ symbols! {
         static_recursion,
         staticlib,
         std,
-        std_inject,
         std_panic,
         std_panic_2015_macro,
         std_panic_macro,
@@ -1449,7 +1406,6 @@ symbols! {
         str_trim_start,
         strict_provenance,
         stringify,
-        stringify_macro,
         struct_field_attributes,
         struct_inherit,
         struct_variant,
@@ -1477,10 +1433,8 @@ symbols! {
         target_has_atomic_load_store,
         target_os,
         target_pointer_width,
-        target_target_vendor,
         target_thread_local,
         target_vendor,
-        task,
         tbm_target_feature,
         termination,
         termination_trait,
@@ -1492,7 +1446,6 @@ symbols! {
         test_removed_feature,
         test_runner,
         test_unstable_lint,
-        then_with,
         thread,
         thread_local,
         thread_local_macro,
@@ -1524,7 +1477,6 @@ symbols! {
         try_trait_v2,
         tt,
         tuple,
-        tuple_from_req,
         tuple_indexing,
         tuple_trait,
         two_phase,
@@ -1568,7 +1520,6 @@ symbols! {
         unreachable_2015,
         unreachable_2015_macro,
         unreachable_2021,
-        unreachable_2021_macro,
         unreachable_code,
         unreachable_display,
         unreachable_macro,
@@ -1587,7 +1538,6 @@ symbols! {
                           from crates.io via `Cargo.toml` instead?",
         untagged_unions,
         unused_imports,
-        unused_qualifications,
         unwind,
         unwind_attributes,
         unwind_safe_trait,
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 7171ca7bf89..decbefc2f7c 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -1083,6 +1083,11 @@ impl Abi {
         }
     }
 
+    #[inline]
+    pub fn is_sized(&self) -> bool {
+        !self.is_unsized()
+    }
+
     /// Returns `true` if this is a single signed integer scalar
     #[inline]
     pub fn is_signed(&self) -> bool {
@@ -1490,6 +1495,11 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         self.abi.is_unsized()
     }
 
+    #[inline]
+    pub fn is_sized(&self) -> bool {
+        self.abi.is_sized()
+    }
+
     /// Returns `true` if the type is a ZST and not unsized.
     pub fn is_zst(&self) -> bool {
         match self.abi {
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index e809f646860..664592b02a1 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -114,7 +114,7 @@ pub enum Lld {
 /// relevant now.
 ///
 /// The second goal is to keep the number of flavors to the minimum if possible.
-/// LLD somewhat forces our hand here because that linker is self-sufficent only if its executable
+/// LLD somewhat forces our hand here because that linker is self-sufficient only if its executable
 /// (`argv[0]`) is named in specific way, otherwise it doesn't work and requires a
 /// `-flavor LLD_FLAVOR` argument to choose which logic to use. Our shipped `rust-lld` in
 /// particular is not named in such specific way, so it needs the flavor option, so we make our
diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs
index 93a956403e5..6f0bbf0672d 100644
--- a/compiler/rustc_target/src/spec/wasm32_wasi.rs
+++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs
@@ -72,7 +72,8 @@
 //! best we can with this target. Don't start relying on too much here unless
 //! you know what you're getting in to!
 
-use super::{crt_objects, wasm_base, Cc, LinkerFlavor, Target};
+use super::crt_objects::{self, LinkSelfContainedDefault};
+use super::{wasm_base, Cc, LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut options = wasm_base::options();
@@ -83,6 +84,9 @@ pub fn target() -> Target {
     options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained();
     options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained();
 
+    // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
+    options.link_self_contained = LinkSelfContainedDefault::True;
+
     // Right now this is a bit of a workaround but we're currently saying that
     // the target by default has a static crt which we're taking as a signal
     // for "use the bundled crt". If that's turned off then the system's crt
diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs
index 528a84a8b37..625d3b37c4f 100644
--- a/compiler/rustc_target/src/spec/wasm_base.rs
+++ b/compiler/rustc_target/src/spec/wasm_base.rs
@@ -1,4 +1,3 @@
-use super::crt_objects::LinkSelfContainedDefault;
 use super::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
 
 pub fn options() -> TargetOptions {
@@ -95,9 +94,6 @@ pub fn options() -> TargetOptions {
 
         pre_link_args,
 
-        // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
-        link_self_contained: LinkSelfContainedDefault::True,
-
         // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when
         // PIC code is implemented this has quite a drastic effect if it stays
         // at the default, `pic`. In an effort to keep wasm binaries as minimal
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 23c3715860e..19f404cb5b7 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -58,10 +58,10 @@ pub struct NoValueInOnUnimplemented {
     pub span: Span,
 }
 
-pub struct NegativePositiveConflict<'a> {
+pub struct NegativePositiveConflict<'tcx> {
     pub impl_span: Span,
-    pub trait_desc: &'a str,
-    pub self_desc: &'a Option<String>,
+    pub trait_desc: ty::TraitRef<'tcx>,
+    pub self_ty: Option<Ty<'tcx>>,
     pub negative_impl_span: Result<Span, Symbol>,
     pub positive_impl_span: Result<Span, Symbol>,
 }
@@ -73,10 +73,10 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> {
         handler: &Handler,
     ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
         let mut diag = handler.struct_err(fluent::trait_selection_negative_positive_conflict);
-        diag.set_arg("trait_desc", self.trait_desc);
+        diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
         diag.set_arg(
             "self_desc",
-            self.self_desc.clone().map_or_else(|| String::from("none"), |ty| ty),
+            self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()),
         );
         diag.set_span(self.impl_span);
         diag.code(rustc_errors::error_code!(E0751));
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 8aab75490a8..3cf2959a9ff 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -64,13 +64,13 @@ pub fn add_placeholder_note(err: &mut Diagnostic) {
 /// with a suitably-freshened `ImplHeader` with those types
 /// substituted. Otherwise, returns `None`.
 #[instrument(skip(tcx, skip_leak_check), level = "debug")]
-pub fn overlapping_impls(
-    tcx: TyCtxt<'_>,
+pub fn overlapping_impls<'tcx>(
+    tcx: TyCtxt<'tcx>,
     impl1_def_id: DefId,
     impl2_def_id: DefId,
     skip_leak_check: SkipLeakCheck,
     overlap_mode: OverlapMode,
-) -> Option<OverlapResult<'_>> {
+) -> Option<OverlapResult<'tcx>> {
     // Before doing expensive operations like entering an inference context, do
     // a quick check via fast_reject to tell if the impl headers could possibly
     // unify.
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 1de85e2f288..db3ddc9208a 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -198,13 +198,6 @@ pub fn is_const_evaluatable<'tcx>(
                     .sess
                     .delay_span_bug(span, "Missing value for constant, but no error reported?"),
             )),
-            Err(ErrorHandled::Linted) => {
-                let reported = infcx
-                    .tcx
-                    .sess
-                    .delay_span_bug(span, "constant in type had error reported as lint");
-                Err(NotConstEvaluatable::Error(reported))
-            }
             Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
             Ok(_) => Ok(()),
         }
@@ -254,11 +247,6 @@ pub fn is_const_evaluatable<'tcx>(
 
                 Err(err)
             },
-            Err(ErrorHandled::Linted) => {
-                let reported =
-                    infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
-                Err(NotConstEvaluatable::Error(reported))
-            }
             Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
             Ok(_) => Ok(()),
         }
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 98c13ffdafb..786cadaa6ce 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -650,41 +650,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 ))
                         );
 
-                        if is_try_conversion {
-                            let none_error = self
-                                .tcx
-                                .get_diagnostic_item(sym::none_error)
-                                .map(|def_id| tcx.type_of(def_id));
-                            let should_convert_option_to_result =
-                                Some(trait_ref.skip_binder().substs.type_at(1)) == none_error;
-                            let should_convert_result_to_option =
-                                Some(trait_ref.self_ty().skip_binder()) == none_error;
-                            if should_convert_option_to_result {
-                                err.span_suggestion_verbose(
-                                    span.shrink_to_lo(),
-                                    "consider converting the `Option<T>` into a `Result<T, _>` \
-                                     using `Option::ok_or` or `Option::ok_or_else`",
-                                    ".ok_or_else(|| /* error value */)",
-                                    Applicability::HasPlaceholders,
-                                );
-                            } else if should_convert_result_to_option {
-                                err.span_suggestion_verbose(
-                                    span.shrink_to_lo(),
-                                    "consider converting the `Result<T, _>` into an `Option<T>` \
-                                     using `Result::ok`",
-                                    ".ok()",
-                                    Applicability::MachineApplicable,
-                                );
-                            }
-                            if let Some(ret_span) = self.return_type_span(&obligation) {
-                                err.span_label(
-                                    ret_span,
-                                    &format!(
-                                        "expected `{}` because of this",
-                                        trait_ref.skip_binder().self_ty()
-                                    ),
-                                );
-                            }
+                        if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
+                            err.span_label(
+                                ret_span,
+                                &format!(
+                                    "expected `{}` because of this",
+                                    trait_ref.skip_binder().self_ty()
+                                ),
+                            );
                         }
 
                         if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() {
@@ -2619,11 +2592,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         let Some(param) = generics.params.iter().find(|param| param.span == span) else {
             return;
         };
-        let param_def_id = self.tcx.hir().local_def_id(param.hir_id);
         // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
         // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
         let explicitly_sized = generics
-            .bounds_for_param(param_def_id)
+            .bounds_for_param(param.def_id)
             .flat_map(|bp| bp.bounds)
             .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
         if explicitly_sized {
@@ -2646,7 +2618,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             _ => {}
         };
         // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
-        let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param_def_id)
+        let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param.def_id)
         {
             (s, " +")
         } else {
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 22e7d3ea5dd..84daaf97ecf 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -44,7 +44,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
 #[derive(Debug)]
 pub enum GeneratorInteriorOrUpvar {
     // span of interior type
-    Interior(Span),
+    Interior(Span, Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>),
     // span of upvar
     Upvar(Span),
 }
@@ -283,7 +283,6 @@ pub trait TypeErrCtxtExt<'tcx> {
         &self,
         err: &mut Diagnostic,
         interior_or_upvar_span: GeneratorInteriorOrUpvar,
-        interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
         is_async: bool,
         outer_generator: Option<DefId>,
         trait_pred: ty::TraitPredicate<'tcx>,
@@ -2003,17 +2002,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             .as_local()
             .and_then(|def_id| hir.maybe_body_owned_by(def_id))
             .map(|body_id| hir.body(body_id));
-        let is_async = match generator_did.as_local() {
-            Some(_) => generator_body
-                .and_then(|body| body.generator_kind())
-                .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..)))
-                .unwrap_or(false),
-            None => self
-                .tcx
-                .generator_kind(generator_did)
-                .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..)))
-                .unwrap_or(false),
-        };
+        let is_async = self
+            .tcx
+            .generator_kind(generator_did)
+            .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..)))
+            .unwrap_or(false);
         let mut visitor = AwaitsVisitor::default();
         if let Some(body) = generator_body {
             visitor.visit_body(body);
@@ -2043,61 +2036,60 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             eq
         };
 
-        let mut interior_or_upvar_span = None;
-        let mut interior_extra_info = None;
-
         // Get the typeck results from the infcx if the generator is the function we are currently
         // type-checking; otherwise, get them by performing a query.  This is needed to avoid
         // cycles. If we can't use resolved types because the generator comes from another crate,
         // we still provide a targeted error but without all the relevant spans.
-        let generator_data: Option<GeneratorData<'tcx, '_>> = match &self.typeck_results {
-            Some(t) if t.hir_owner.to_def_id() == generator_did_root => {
-                Some(GeneratorData::Local(&t))
-            }
+        let generator_data = match &self.typeck_results {
+            Some(t) if t.hir_owner.to_def_id() == generator_did_root => GeneratorData::Local(&t),
             _ if generator_did.is_local() => {
-                Some(GeneratorData::Local(self.tcx.typeck(generator_did.expect_local())))
+                GeneratorData::Local(self.tcx.typeck(generator_did.expect_local()))
             }
-            _ => self
-                .tcx
-                .generator_diagnostic_data(generator_did)
-                .as_ref()
-                .map(|generator_diag_data| GeneratorData::Foreign(generator_diag_data)),
+            _ if let Some(generator_diag_data) = self.tcx.generator_diagnostic_data(generator_did) => {
+                GeneratorData::Foreign(generator_diag_data)
+            }
+            _ => return false,
         };
 
-        if let Some(generator_data) = generator_data.as_ref() {
-            interior_or_upvar_span =
-                generator_data.try_get_upvar_span(&self, generator_did, ty_matches);
+        let mut interior_or_upvar_span = None;
 
-            // The generator interior types share the same binders
-            if let Some(cause) =
-                generator_data.get_generator_interior_types().skip_binder().iter().find(
-                    |ty::GeneratorInteriorTypeCause { ty, .. }| {
-                        ty_matches(generator_data.get_generator_interior_types().rebind(*ty))
-                    },
-                )
-            {
-                let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches);
-                let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } =
-                    cause;
+        let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches);
+        debug!(?from_awaited_ty);
 
-                interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span));
-                interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty));
-            }
+        // The generator interior types share the same binders
+        if let Some(cause) =
+            generator_data.get_generator_interior_types().skip_binder().iter().find(
+                |ty::GeneratorInteriorTypeCause { ty, .. }| {
+                    ty_matches(generator_data.get_generator_interior_types().rebind(*ty))
+                },
+            )
+        {
+            let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = cause;
 
-            if interior_or_upvar_span.is_none() && generator_data.is_foreign() {
-                interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span));
-            }
+            interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(
+                *span,
+                Some((*scope_span, *yield_span, *expr, from_awaited_ty)),
+            ));
         }
 
+        if interior_or_upvar_span.is_none() {
+            interior_or_upvar_span =
+                generator_data.try_get_upvar_span(&self, generator_did, ty_matches);
+        }
+
+        if interior_or_upvar_span.is_none() && generator_data.is_foreign() {
+            interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span, None));
+        }
+
+        debug!(?interior_or_upvar_span);
         if let Some(interior_or_upvar_span) = interior_or_upvar_span {
-            let typeck_results = generator_data.and_then(|generator_data| match generator_data {
+            let typeck_results = match generator_data {
                 GeneratorData::Local(typeck_results) => Some(typeck_results),
                 GeneratorData::Foreign(_) => None,
-            });
+            };
             self.note_obligation_cause_for_async_await(
                 err,
                 interior_or_upvar_span,
-                interior_extra_info,
                 is_async,
                 outer_generator,
                 trait_ref,
@@ -2119,7 +2111,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         &self,
         err: &mut Diagnostic,
         interior_or_upvar_span: GeneratorInteriorOrUpvar,
-        interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
         is_async: bool,
         outer_generator: Option<DefId>,
         trait_pred: ty::TraitPredicate<'tcx>,
@@ -2241,7 +2232,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
         };
         match interior_or_upvar_span {
-            GeneratorInteriorOrUpvar::Interior(interior_span) => {
+            GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => {
                 if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info {
                     if let Some(await_span) = from_awaited_ty {
                         // The type causing this obligation is one being awaited at await_span.
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index b486c07f354..4905bb69cc5 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -558,12 +558,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                 NotConstEvaluatable::Error(reported),
                             )),
                         ),
-                        (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => {
-                            span_bug!(
-                                obligation.cause.span(),
-                                "ConstEquate: const_eval_resolve returned an unexpected error"
-                            )
-                        }
                         (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
                             if c1.has_non_region_infer() || c2.has_non_region_infer() {
                                 ProcessResult::Unchanged
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index a12f67125bb..3a899c03b4c 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -727,12 +727,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         }
                         (Err(ErrorHandled::Reported(_)), _)
                         | (_, Err(ErrorHandled::Reported(_))) => Ok(EvaluatedToErr),
-                        (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => {
-                            span_bug!(
-                                obligation.cause.span(),
-                                "ConstEquate: const_eval_resolve returned an unexpected error"
-                            )
-                        }
                         (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
                             if c1.has_non_region_infer() || c2.has_non_region_infer() {
                                 Ok(EvaluatedToAmbig)
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 231a18f86ea..7cc12eff20e 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -19,9 +19,9 @@ use crate::traits::engine::TraitEngineExt as _;
 use crate::traits::select::IntercrateAmbiguityCause;
 use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause};
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{struct_span_err, DiagnosticBuilder, EmissionGuarantee};
+use rustc_errors::{error_code, DelayDm, Diagnostic};
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_middle::ty::{self, ImplSubject, TyCtxt};
+use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt};
 use rustc_middle::ty::{InternalSubsts, SubstsRef};
 use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
 use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
@@ -32,10 +32,10 @@ use super::SelectionContext;
 
 /// Information pertinent to an overlapping impl error.
 #[derive(Debug)]
-pub struct OverlapError {
+pub struct OverlapError<'tcx> {
     pub with_impl: DefId,
-    pub trait_desc: String,
-    pub self_desc: Option<String>,
+    pub trait_ref: ty::TraitRef<'tcx>,
+    pub self_ty: Option<Ty<'tcx>>,
     pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause>,
     pub involves_placeholder: bool,
 }
@@ -275,9 +275,9 @@ pub(super) fn specialization_graph_provider(
 // it negatively impacts perf.
 #[cold]
 #[inline(never)]
-fn report_overlap_conflict(
-    tcx: TyCtxt<'_>,
-    overlap: OverlapError,
+fn report_overlap_conflict<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    overlap: OverlapError<'tcx>,
     impl_def_id: LocalDefId,
     used_to_be_allowed: Option<FutureCompatOverlapErrorKind>,
     sg: &mut specialization_graph::Graph,
@@ -313,9 +313,9 @@ fn report_overlap_conflict(
     }
 }
 
-fn report_negative_positive_conflict(
-    tcx: TyCtxt<'_>,
-    overlap: &OverlapError,
+fn report_negative_positive_conflict<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    overlap: &OverlapError<'tcx>,
     local_impl_def_id: LocalDefId,
     negative_impl_def_id: DefId,
     positive_impl_def_id: DefId,
@@ -323,17 +323,17 @@ fn report_negative_positive_conflict(
 ) {
     let mut err = tcx.sess.create_err(NegativePositiveConflict {
         impl_span: tcx.def_span(local_impl_def_id),
-        trait_desc: &overlap.trait_desc,
-        self_desc: &overlap.self_desc,
+        trait_desc: overlap.trait_ref,
+        self_ty: overlap.self_ty,
         negative_impl_span: tcx.span_of_impl(negative_impl_def_id),
         positive_impl_span: tcx.span_of_impl(positive_impl_def_id),
     });
     sg.has_errored = Some(err.emit());
 }
 
-fn report_conflicting_impls(
-    tcx: TyCtxt<'_>,
-    overlap: OverlapError,
+fn report_conflicting_impls<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    overlap: OverlapError<'tcx>,
     impl_def_id: LocalDefId,
     used_to_be_allowed: Option<FutureCompatOverlapErrorKind>,
     sg: &mut specialization_graph::Graph,
@@ -343,12 +343,12 @@ fn report_conflicting_impls(
     // Work to be done after we've built the DiagnosticBuilder. We have to define it
     // now because the struct_lint methods don't return back the DiagnosticBuilder
     // that's passed in.
-    fn decorate<'a, 'b, G: EmissionGuarantee>(
-        tcx: TyCtxt<'_>,
-        overlap: OverlapError,
+    fn decorate<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        overlap: &OverlapError<'tcx>,
         impl_span: Span,
-        err: &'b mut DiagnosticBuilder<'a, G>,
-    ) -> &'b mut DiagnosticBuilder<'a, G> {
+        err: &mut Diagnostic,
+    ) {
         match tcx.span_of_impl(overlap.with_impl) {
             Ok(span) => {
                 err.span_label(span, "first implementation here");
@@ -357,7 +357,7 @@ fn report_conflicting_impls(
                     impl_span,
                     format!(
                         "conflicting implementation{}",
-                        overlap.self_desc.map_or_else(String::new, |ty| format!(" for `{}`", ty))
+                        overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{}`", ty))
                     ),
                 );
             }
@@ -379,26 +379,28 @@ fn report_conflicting_impls(
         if overlap.involves_placeholder {
             coherence::add_placeholder_note(err);
         }
-        err
     }
 
-    let msg = format!(
-        "conflicting implementations of trait `{}`{}{}",
-        overlap.trait_desc,
-        overlap.self_desc.as_deref().map_or_else(String::new, |ty| format!(" for type `{ty}`")),
-        match used_to_be_allowed {
-            Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)",
-            _ => "",
-        }
-    );
+    let msg = DelayDm(|| {
+        format!(
+            "conflicting implementations of trait `{}`{}{}",
+            overlap.trait_ref.print_only_trait_path(),
+            overlap.self_ty.map_or_else(String::new, |ty| format!(" for type `{ty}`")),
+            match used_to_be_allowed {
+                Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)",
+                _ => "",
+            }
+        )
+    });
 
     match used_to_be_allowed {
         None => {
             let reported = if overlap.with_impl.is_local()
                 || tcx.orphan_check_impl(impl_def_id).is_ok()
             {
-                let mut err = struct_span_err!(tcx.sess, impl_span, E0119, "{msg}",);
-                decorate(tcx, overlap, impl_span, &mut err);
+                let mut err = tcx.sess.struct_span_err(impl_span, msg);
+                err.code(error_code!(E0119));
+                decorate(tcx, &overlap, impl_span, &mut err);
                 Some(err.emit())
             } else {
                 Some(tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check"))
@@ -415,7 +417,10 @@ fn report_conflicting_impls(
                 tcx.hir().local_def_id_to_hir_id(impl_def_id),
                 impl_span,
                 msg,
-                |err| decorate(tcx, overlap, impl_span, err),
+                |err| {
+                    decorate(tcx, &overlap, impl_span, err);
+                    err
+                },
             );
         }
     };
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
index 63f89a33e8a..4546c953393 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
@@ -3,7 +3,6 @@ use super::OverlapError;
 use crate::traits;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
-use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
 
 pub use rustc_middle::traits::specialization_graph::*;
@@ -15,15 +14,15 @@ pub enum FutureCompatOverlapErrorKind {
 }
 
 #[derive(Debug)]
-pub struct FutureCompatOverlapError {
-    pub error: OverlapError,
+pub struct FutureCompatOverlapError<'tcx> {
+    pub error: OverlapError<'tcx>,
     pub kind: FutureCompatOverlapErrorKind,
 }
 
 /// The result of attempting to insert an impl into a group of children.
-enum Inserted {
+enum Inserted<'tcx> {
     /// The impl was inserted as a new child in this group of children.
-    BecameNewSibling(Option<FutureCompatOverlapError>),
+    BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>),
 
     /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc.
     ReplaceChildren(Vec<DefId>),
@@ -42,12 +41,12 @@ trait ChildrenExt<'tcx> {
         impl_def_id: DefId,
         simplified_self: Option<SimplifiedType>,
         overlap_mode: OverlapMode,
-    ) -> Result<Inserted, OverlapError>;
+    ) -> Result<Inserted<'tcx>, OverlapError<'tcx>>;
 }
 
-impl ChildrenExt<'_> for Children {
+impl<'tcx> ChildrenExt<'tcx> for Children {
     /// Insert an impl into this set of children without comparing to any existing impls.
-    fn insert_blindly(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) {
+    fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
         let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
         if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer)
         {
@@ -62,7 +61,7 @@ impl ChildrenExt<'_> for Children {
     /// Removes an impl from this set of children. Used when replacing
     /// an impl with a parent. The impl must be present in the list of
     /// children already.
-    fn remove_existing(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) {
+    fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
         let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
         let vec: &mut Vec<DefId>;
         if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer)
@@ -82,11 +81,11 @@ impl ChildrenExt<'_> for Children {
     /// specialization relationships.
     fn insert(
         &mut self,
-        tcx: TyCtxt<'_>,
+        tcx: TyCtxt<'tcx>,
         impl_def_id: DefId,
         simplified_self: Option<SimplifiedType>,
         overlap_mode: OverlapMode,
-    ) -> Result<Inserted, OverlapError> {
+    ) -> Result<Inserted<'tcx>, OverlapError<'tcx>> {
         let mut last_lint = None;
         let mut replace_children = Vec::new();
 
@@ -103,30 +102,23 @@ impl ChildrenExt<'_> for Children {
                 impl_def_id, simplified_self, possible_sibling,
             );
 
-            let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| {
+            let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| {
                 let trait_ref = overlap.impl_header.trait_ref.unwrap();
                 let self_ty = trait_ref.self_ty();
 
-                // FIXME: should postpone string formatting until we decide to actually emit.
-                with_no_trimmed_paths!({
-                    OverlapError {
-                        with_impl: possible_sibling,
-                        trait_desc: trait_ref.print_only_trait_path().to_string(),
-                        // Only report the `Self` type if it has at least
-                        // some outer concrete shell; otherwise, it's
-                        // not adding much information.
-                        self_desc: if self_ty.has_concrete_skeleton() {
-                            Some(self_ty.to_string())
-                        } else {
-                            None
-                        },
-                        intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
-                        involves_placeholder: overlap.involves_placeholder,
-                    }
-                })
+                OverlapError {
+                    with_impl: possible_sibling,
+                    trait_ref,
+                    // Only report the `Self` type if it has at least
+                    // some outer concrete shell; otherwise, it's
+                    // not adding much information.
+                    self_ty: if self_ty.has_concrete_skeleton() { Some(self_ty) } else { None },
+                    intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
+                    involves_placeholder: overlap.involves_placeholder,
+                }
             };
 
-            let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>,
+            let report_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>,
                                         last_lint: &mut _| {
                 // Found overlap, but no specialization; error out or report future-compat warning.
 
@@ -255,31 +247,31 @@ where
     }
 }
 
-pub trait GraphExt {
+pub trait GraphExt<'tcx> {
     /// Insert a local impl into the specialization graph. If an existing impl
     /// conflicts with it (has overlap, but neither specializes the other),
     /// information about the area of overlap is returned in the `Err`.
     fn insert(
         &mut self,
-        tcx: TyCtxt<'_>,
+        tcx: TyCtxt<'tcx>,
         impl_def_id: DefId,
         overlap_mode: OverlapMode,
-    ) -> Result<Option<FutureCompatOverlapError>, OverlapError>;
+    ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>;
 
     /// Insert cached metadata mapping from a child impl back to its parent.
-    fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'_>, parent: DefId, child: DefId);
+    fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId);
 }
 
-impl GraphExt for Graph {
+impl<'tcx> GraphExt<'tcx> for Graph {
     /// Insert a local impl into the specialization graph. If an existing impl
     /// conflicts with it (has overlap, but neither specializes the other),
     /// information about the area of overlap is returned in the `Err`.
     fn insert(
         &mut self,
-        tcx: TyCtxt<'_>,
+        tcx: TyCtxt<'tcx>,
         impl_def_id: DefId,
         overlap_mode: OverlapMode,
-    ) -> Result<Option<FutureCompatOverlapError>, OverlapError> {
+    ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>> {
         assert!(impl_def_id.is_local());
 
         let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
@@ -376,7 +368,7 @@ impl GraphExt for Graph {
     }
 
     /// Insert cached metadata mapping from a child impl back to its parent.
-    fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'_>, parent: DefId, child: DefId) {
+    fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) {
         if self.parent.insert(child, parent).is_some() {
             bug!(
                 "When recording an impl from the crate store, information about its parent \
diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml
index 951554c77fb..9474e6df567 100644
--- a/compiler/rustc_traits/Cargo.toml
+++ b/compiler/rustc_traits/Cargo.toml
@@ -12,9 +12,9 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_span = { path = "../rustc_span" }
-chalk-ir = "0.80.0"
-chalk-engine = "0.80.0"
-chalk-solve = "0.80.0"
+chalk-ir = "0.87.0"
+chalk-engine = "0.87.0"
+chalk-solve = "0.87.0"
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 rustc_infer = { path = "../rustc_infer" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs
index 07f92299f72..d15707e5ced 100644
--- a/compiler/rustc_traits/src/chalk/db.rs
+++ b/compiler/rustc_traits/src/chalk/db.rs
@@ -142,6 +142,8 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
             Some(CoerceUnsized)
         } else if lang_items.dispatch_from_dyn_trait() == Some(def_id) {
             Some(DispatchFromDyn)
+        } else if lang_items.tuple_trait() == Some(def_id) {
+            Some(Tuple)
         } else {
             None
         };
@@ -570,6 +572,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
             CoerceUnsized => lang_items.coerce_unsized_trait(),
             DiscriminantKind => lang_items.discriminant_kind_trait(),
             DispatchFromDyn => lang_items.dispatch_from_dyn_trait(),
+            Tuple => lang_items.tuple_trait(),
         };
         def_id.map(chalk_ir::TraitId)
     }
diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs
index b64d53e60de..25cedefa261 100644
--- a/compiler/rustc_traits/src/chalk/lowering.rs
+++ b/compiler/rustc_traits/src/chalk/lowering.rs
@@ -507,9 +507,6 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'t
                 name: ty::BoundRegionKind::BrAnon(p.idx as u32, None),
             }),
             chalk_ir::LifetimeData::Static => return interner.tcx.lifetimes.re_static,
-            chalk_ir::LifetimeData::Empty(_) => {
-                bug!("Chalk should not have been passed an empty lifetime.")
-            }
             chalk_ir::LifetimeData::Erased => return interner.tcx.lifetimes.re_erased,
             chalk_ir::LifetimeData::Phantom(void, _) => match *void {},
         };
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 52ba0eee97c..b59be0a0ea7 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -668,7 +668,7 @@ fn layout_of_uncached<'tcx>(
                 let mut abi = Abi::Aggregate { sized: true };
                 let index = VariantIdx::new(0);
                 for field in &variants[index] {
-                    assert!(!field.is_unsized());
+                    assert!(field.is_sized());
                     align = align.max(field.align);
 
                     // If all non-ZST fields have the same ABI, forward this ABI
diff --git a/library/alloc/benches/str.rs b/library/alloc/benches/str.rs
index 391475bc0c7..54af389dedc 100644
--- a/library/alloc/benches/str.rs
+++ b/library/alloc/benches/str.rs
@@ -1,3 +1,4 @@
+use core::iter::Iterator;
 use test::{black_box, Bencher};
 
 #[bench]
@@ -122,14 +123,13 @@ fn bench_contains_short_short(b: &mut Bencher) {
     let haystack = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
     let needle = "sit";
 
+    b.bytes = haystack.len() as u64;
     b.iter(|| {
-        assert!(haystack.contains(needle));
+        assert!(black_box(haystack).contains(black_box(needle)));
     })
 }
 
-#[bench]
-fn bench_contains_short_long(b: &mut Bencher) {
-    let haystack = "\
+static LONG_HAYSTACK: &str = "\
 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \
 ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \
 eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \
@@ -164,10 +164,48 @@ feugiat. Etiam quis mauris vel risus luctus mattis a a nunc. Nullam orci quam, i
 vehicula in, porttitor ut nibh. Duis sagittis adipiscing nisl vitae congue. Donec mollis risus eu \
 leo suscipit, varius porttitor nulla porta. Pellentesque ut sem nec nisi euismod vehicula. Nulla \
 malesuada sollicitudin quam eu fermentum.";
+
+#[bench]
+fn bench_contains_2b_repeated_long(b: &mut Bencher) {
+    let haystack = LONG_HAYSTACK;
+    let needle = "::";
+
+    b.bytes = haystack.len() as u64;
+    b.iter(|| {
+        assert!(!black_box(haystack).contains(black_box(needle)));
+    })
+}
+
+#[bench]
+fn bench_contains_short_long(b: &mut Bencher) {
+    let haystack = LONG_HAYSTACK;
     let needle = "english";
 
+    b.bytes = haystack.len() as u64;
+    b.iter(|| {
+        assert!(!black_box(haystack).contains(black_box(needle)));
+    })
+}
+
+#[bench]
+fn bench_contains_16b_in_long(b: &mut Bencher) {
+    let haystack = LONG_HAYSTACK;
+    let needle = "english language";
+
+    b.bytes = haystack.len() as u64;
+    b.iter(|| {
+        assert!(!black_box(haystack).contains(black_box(needle)));
+    })
+}
+
+#[bench]
+fn bench_contains_32b_in_long(b: &mut Bencher) {
+    let haystack = LONG_HAYSTACK;
+    let needle = "the english language sample text";
+
+    b.bytes = haystack.len() as u64;
     b.iter(|| {
-        assert!(!haystack.contains(needle));
+        assert!(!black_box(haystack).contains(black_box(needle)));
     })
 }
 
@@ -176,8 +214,20 @@ fn bench_contains_bad_naive(b: &mut Bencher) {
     let haystack = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
     let needle = "aaaaaaaab";
 
+    b.bytes = haystack.len() as u64;
+    b.iter(|| {
+        assert!(!black_box(haystack).contains(black_box(needle)));
+    })
+}
+
+#[bench]
+fn bench_contains_bad_simd(b: &mut Bencher) {
+    let haystack = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+    let needle = "aaabaaaa";
+
+    b.bytes = haystack.len() as u64;
     b.iter(|| {
-        assert!(!haystack.contains(needle));
+        assert!(!black_box(haystack).contains(black_box(needle)));
     })
 }
 
@@ -186,8 +236,9 @@ fn bench_contains_equal(b: &mut Bencher) {
     let haystack = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
     let needle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
 
+    b.bytes = haystack.len() as u64;
     b.iter(|| {
-        assert!(haystack.contains(needle));
+        assert!(black_box(haystack).contains(black_box(needle)));
     })
 }
 
diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs
index 8c6663569a5..e5fbfc55761 100644
--- a/library/alloc/src/alloc.rs
+++ b/library/alloc/src/alloc.rs
@@ -28,20 +28,16 @@ extern "Rust" {
     // The rustc fork of LLVM 14 and earlier also special-cases these function names to be able to optimize them
     // like `malloc`, `realloc`, and `free`, respectively.
     #[rustc_allocator]
-    #[cfg_attr(not(bootstrap), rustc_nounwind)]
-    #[cfg_attr(bootstrap, rustc_allocator_nounwind)]
+    #[rustc_nounwind]
     fn __rust_alloc(size: usize, align: usize) -> *mut u8;
     #[rustc_deallocator]
-    #[cfg_attr(not(bootstrap), rustc_nounwind)]
-    #[cfg_attr(bootstrap, rustc_allocator_nounwind)]
+    #[rustc_nounwind]
     fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
     #[rustc_reallocator]
-    #[cfg_attr(not(bootstrap), rustc_nounwind)]
-    #[cfg_attr(bootstrap, rustc_allocator_nounwind)]
+    #[rustc_nounwind]
     fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
     #[rustc_allocator_zeroed]
-    #[cfg_attr(not(bootstrap), rustc_nounwind)]
-    #[cfg_attr(bootstrap, rustc_allocator_nounwind)]
+    #[rustc_nounwind]
     fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
 }
 
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs
index 66f4c19e0f9..97186589a4f 100644
--- a/library/alloc/src/boxed.rs
+++ b/library/alloc/src/boxed.rs
@@ -1661,7 +1661,7 @@ impl<T, const N: usize> TryFrom<Box<[T]>> for Box<[T; N]> {
 }
 
 #[cfg(not(no_global_oom_handling))]
-#[stable(feature = "boxed_array_try_from_vec", since = "CURRENT_RUSTC_VERSION")]
+#[stable(feature = "boxed_array_try_from_vec", since = "1.66.0")]
 impl<T, const N: usize> TryFrom<Vec<T>> for Box<[T; N]> {
     type Error = Vec<T>;
 
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index c4c75e46a2a..8a771934712 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -580,7 +580,7 @@ impl<K, V> BTreeMap<K, V> {
     /// map.insert(1, "a");
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_stable(feature = "const_btree_new", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "const_btree_new", since = "1.66.0")]
     #[must_use]
     pub const fn new() -> BTreeMap<K, V> {
         BTreeMap { root: None, length: 0, alloc: ManuallyDrop::new(Global), _marker: PhantomData }
@@ -711,7 +711,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// map.insert(2, "a");
     /// assert_eq!(map.first_key_value(), Some((&1, &"b")));
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn first_key_value(&self) -> Option<(&K, &V)>
     where
         K: Ord,
@@ -739,7 +739,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// assert_eq!(*map.get(&1).unwrap(), "first");
     /// assert_eq!(*map.get(&2).unwrap(), "b");
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn first_entry(&mut self) -> Option<OccupiedEntry<'_, K, V, A>>
     where
         K: Ord,
@@ -773,7 +773,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// }
     /// assert!(map.is_empty());
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn pop_first(&mut self) -> Option<(K, V)>
     where
         K: Ord,
@@ -796,7 +796,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// map.insert(2, "a");
     /// assert_eq!(map.last_key_value(), Some((&2, &"a")));
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn last_key_value(&self) -> Option<(&K, &V)>
     where
         K: Ord,
@@ -824,7 +824,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// assert_eq!(*map.get(&1).unwrap(), "a");
     /// assert_eq!(*map.get(&2).unwrap(), "last");
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn last_entry(&mut self) -> Option<OccupiedEntry<'_, K, V, A>>
     where
         K: Ord,
@@ -858,7 +858,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// }
     /// assert!(map.is_empty());
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn pop_last(&mut self) -> Option<(K, V)>
     where
         K: Ord,
diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs
index b8e5cf8eb5a..4ddb2119252 100644
--- a/library/alloc/src/collections/btree/set.rs
+++ b/library/alloc/src/collections/btree/set.rs
@@ -343,7 +343,7 @@ impl<T> BTreeSet<T> {
     /// let mut set: BTreeSet<i32> = BTreeSet::new();
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_stable(feature = "const_btree_new", since = "CURRENT_RUSTC_VERSION")]
+    #[rustc_const_stable(feature = "const_btree_new", since = "1.66.0")]
     #[must_use]
     pub const fn new() -> BTreeSet<T> {
         BTreeSet { map: BTreeMap::new() }
@@ -796,7 +796,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// assert_eq!(set.first(), Some(&1));
     /// ```
     #[must_use]
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn first(&self) -> Option<&T>
     where
         T: Ord,
@@ -822,7 +822,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// assert_eq!(set.last(), Some(&2));
     /// ```
     #[must_use]
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn last(&self) -> Option<&T>
     where
         T: Ord,
@@ -846,7 +846,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// }
     /// assert!(set.is_empty());
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn pop_first(&mut self) -> Option<T>
     where
         T: Ord,
@@ -870,7 +870,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> {
     /// }
     /// assert!(set.is_empty());
     /// ```
-    #[stable(feature = "map_first_last", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "map_first_last", since = "1.66.0")]
     pub fn pop_last(&mut self) -> Option<T>
     where
         T: Ord,
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index 006d813e5f9..f3cbfe27b3e 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -293,6 +293,15 @@ struct RcBox<T: ?Sized> {
     value: T,
 }
 
+/// Calculate layout for `RcBox<T>` using the inner value's layout
+fn rcbox_layout_for_value_layout(layout: Layout) -> Layout {
+    // Calculate layout using the given value layout.
+    // Previously, layout was calculated on the expression
+    // `&*(ptr as *const RcBox<T>)`, but this created a misaligned
+    // reference (see #54908).
+    Layout::new::<RcBox<()>>().extend(layout).unwrap().0.pad_to_align()
+}
+
 /// A single-threaded reference-counting pointer. 'Rc' stands for 'Reference
 /// Counted'.
 ///
@@ -1334,11 +1343,7 @@ impl<T: ?Sized> Rc<T> {
         allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
         mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>,
     ) -> *mut RcBox<T> {
-        // Calculate layout using the given value layout.
-        // Previously, layout was calculated on the expression
-        // `&*(ptr as *const RcBox<T>)`, but this created a misaligned
-        // reference (see #54908).
-        let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
+        let layout = rcbox_layout_for_value_layout(value_layout);
         unsafe {
             Rc::try_allocate_for_layout(value_layout, allocate, mem_to_rcbox)
                 .unwrap_or_else(|_| handle_alloc_error(layout))
@@ -1357,11 +1362,7 @@ impl<T: ?Sized> Rc<T> {
         allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
         mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>,
     ) -> Result<*mut RcBox<T>, AllocError> {
-        // Calculate layout using the given value layout.
-        // Previously, layout was calculated on the expression
-        // `&*(ptr as *const RcBox<T>)`, but this created a misaligned
-        // reference (see #54908).
-        let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
+        let layout = rcbox_layout_for_value_layout(value_layout);
 
         // Allocate for the layout.
         let ptr = allocate(layout)?;
@@ -1428,7 +1429,7 @@ impl<T> Rc<[T]> {
         }
     }
 
-    /// Copy elements from slice into newly allocated Rc<\[T\]>
+    /// Copy elements from slice into newly allocated `Rc<[T]>`
     ///
     /// Unsafe because the caller must either take ownership or bind `T: Copy`
     #[cfg(not(no_global_oom_handling))]
@@ -1440,6 +1441,48 @@ impl<T> Rc<[T]> {
         }
     }
 
+    /// Create an `Rc<[T]>` by reusing the underlying memory
+    /// of a `Vec<T>`. This will return the vector if the existing allocation
+    /// is not large enough.
+    #[cfg(not(no_global_oom_handling))]
+    fn try_from_vec_in_place(mut v: Vec<T>) -> Result<Rc<[T]>, Vec<T>> {
+        let layout_elements = Layout::array::<T>(v.len()).unwrap();
+        let layout_allocation = Layout::array::<T>(v.capacity()).unwrap();
+        let layout_rcbox = rcbox_layout_for_value_layout(layout_elements);
+        let mut ptr = NonNull::new(v.as_mut_ptr()).expect("`Vec<T>` stores `NonNull<T>`");
+        if layout_rcbox.size() > layout_allocation.size()
+            || layout_rcbox.align() > layout_allocation.align()
+        {
+            // Can't fit - calling `grow` would involve `realloc`
+            // (which copies the elements), followed by copying again.
+            return Err(v);
+        }
+        if layout_rcbox.size() < layout_allocation.size()
+            || layout_rcbox.align() < layout_allocation.align()
+        {
+            // We need to shrink the allocation so that it fits
+            // https://doc.rust-lang.org/nightly/std/alloc/trait.Allocator.html#memory-fitting
+            // SAFETY:
+            // - Vec allocates by requesting `Layout::array::<T>(capacity)`, so this capacity matches
+            // - `layout_rcbox` is smaller
+            // If this fails, the ownership has not been transferred
+            if let Ok(p) = unsafe { Global.shrink(ptr.cast(), layout_allocation, layout_rcbox) } {
+                ptr = p.cast();
+            } else {
+                return Err(v);
+            }
+        }
+        // Make sure the vec's memory isn't deallocated now
+        let v = mem::ManuallyDrop::new(v);
+        let ptr: *mut RcBox<[T]> = ptr::slice_from_raw_parts_mut(ptr.as_ptr(), v.len()) as _;
+        unsafe {
+            ptr::copy(ptr.cast::<T>(), &mut (*ptr).value as *mut [T] as *mut T, v.len());
+            ptr::write(&mut (*ptr).strong, Cell::new(1));
+            ptr::write(&mut (*ptr).weak, Cell::new(1));
+            Ok(Self::from_ptr(ptr))
+        }
+    }
+
     /// Constructs an `Rc<[T]>` from an iterator known to be of a certain size.
     ///
     /// Behavior is undefined should the size be wrong.
@@ -1965,14 +2008,17 @@ impl<T> From<Vec<T>> for Rc<[T]> {
     /// assert_eq!(vec![1, 2, 3], *shared);
     /// ```
     #[inline]
-    fn from(mut v: Vec<T>) -> Rc<[T]> {
-        unsafe {
-            let rc = Rc::copy_from_slice(&v);
-
-            // Allow the Vec to free its memory, but not destroy its contents
-            v.set_len(0);
-
-            rc
+    fn from(v: Vec<T>) -> Rc<[T]> {
+        match Rc::try_from_vec_in_place(v) {
+            Ok(rc) => rc,
+            Err(mut v) => {
+                unsafe {
+                    let rc = Rc::copy_from_slice(&v);
+                    // Allow the Vec to free its memory, but not destroy its contents
+                    v.set_len(0);
+                    rc
+                }
+            }
         }
     }
 }
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index 81cd7707488..37e07eb5998 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -333,6 +333,15 @@ struct ArcInner<T: ?Sized> {
     data: T,
 }
 
+/// Calculate layout for `ArcInner<T>` using the inner value's layout
+fn arcinner_layout_for_value_layout(layout: Layout) -> Layout {
+    // Calculate layout using the given value layout.
+    // Previously, layout was calculated on the expression
+    // `&*(ptr as *const ArcInner<T>)`, but this created a misaligned
+    // reference (see #54908).
+    Layout::new::<ArcInner<()>>().extend(layout).unwrap().0.pad_to_align()
+}
+
 unsafe impl<T: ?Sized + Sync + Send> Send for ArcInner<T> {}
 unsafe impl<T: ?Sized + Sync + Send> Sync for ArcInner<T> {}
 
@@ -1154,11 +1163,7 @@ impl<T: ?Sized> Arc<T> {
         allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
         mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>,
     ) -> *mut ArcInner<T> {
-        // Calculate layout using the given value layout.
-        // Previously, layout was calculated on the expression
-        // `&*(ptr as *const ArcInner<T>)`, but this created a misaligned
-        // reference (see #54908).
-        let layout = Layout::new::<ArcInner<()>>().extend(value_layout).unwrap().0.pad_to_align();
+        let layout = arcinner_layout_for_value_layout(value_layout);
         unsafe {
             Arc::try_allocate_for_layout(value_layout, allocate, mem_to_arcinner)
                 .unwrap_or_else(|_| handle_alloc_error(layout))
@@ -1176,11 +1181,7 @@ impl<T: ?Sized> Arc<T> {
         allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
         mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>,
     ) -> Result<*mut ArcInner<T>, AllocError> {
-        // Calculate layout using the given value layout.
-        // Previously, layout was calculated on the expression
-        // `&*(ptr as *const ArcInner<T>)`, but this created a misaligned
-        // reference (see #54908).
-        let layout = Layout::new::<ArcInner<()>>().extend(value_layout).unwrap().0.pad_to_align();
+        let layout = arcinner_layout_for_value_layout(value_layout);
 
         let ptr = allocate(layout)?;
 
@@ -1246,7 +1247,7 @@ impl<T> Arc<[T]> {
         }
     }
 
-    /// Copy elements from slice into newly allocated Arc<\[T\]>
+    /// Copy elements from slice into newly allocated `Arc<[T]>`
     ///
     /// Unsafe because the caller must either take ownership or bind `T: Copy`.
     #[cfg(not(no_global_oom_handling))]
@@ -1260,6 +1261,49 @@ impl<T> Arc<[T]> {
         }
     }
 
+    /// Create an `Arc<[T]>` by reusing the underlying memory
+    /// of a `Vec<T>`. This will return the vector if the existing allocation
+    /// is not large enough.
+    #[cfg(not(no_global_oom_handling))]
+    fn try_from_vec_in_place(mut v: Vec<T>) -> Result<Arc<[T]>, Vec<T>> {
+        let layout_elements = Layout::array::<T>(v.len()).unwrap();
+        let layout_allocation = Layout::array::<T>(v.capacity()).unwrap();
+        let layout_arcinner = arcinner_layout_for_value_layout(layout_elements);
+        let mut ptr = NonNull::new(v.as_mut_ptr()).expect("`Vec<T>` stores `NonNull<T>`");
+        if layout_arcinner.size() > layout_allocation.size()
+            || layout_arcinner.align() > layout_allocation.align()
+        {
+            // Can't fit - calling `grow` would involve `realloc`
+            // (which copies the elements), followed by copying again.
+            return Err(v);
+        }
+        if layout_arcinner.size() < layout_allocation.size()
+            || layout_arcinner.align() < layout_allocation.align()
+        {
+            // We need to shrink the allocation so that it fits
+            // https://doc.rust-lang.org/nightly/std/alloc/trait.Allocator.html#memory-fitting
+            // SAFETY:
+            // - Vec allocates by requesting `Layout::array::<T>(capacity)`, so this capacity matches
+            // - `layout_arcinner` is smaller
+            // If this fails, the ownership has not been transferred
+            if let Ok(p) = unsafe { Global.shrink(ptr.cast(), layout_allocation, layout_arcinner) }
+            {
+                ptr = p.cast();
+            } else {
+                return Err(v);
+            }
+        }
+        // Make sure the vec's memory isn't deallocated now
+        let v = mem::ManuallyDrop::new(v);
+        let ptr: *mut ArcInner<[T]> = ptr::slice_from_raw_parts_mut(ptr.as_ptr(), v.len()) as _;
+        unsafe {
+            ptr::copy(ptr.cast::<T>(), &mut (*ptr).data as *mut [T] as *mut T, v.len());
+            ptr::write(&mut (*ptr).strong, atomic::AtomicUsize::new(1));
+            ptr::write(&mut (*ptr).weak, atomic::AtomicUsize::new(1));
+            Ok(Self::from_ptr(ptr))
+        }
+    }
+
     /// Constructs an `Arc<[T]>` from an iterator known to be of a certain size.
     ///
     /// Behavior is undefined should the size be wrong.
@@ -2571,14 +2615,17 @@ impl<T> From<Vec<T>> for Arc<[T]> {
     /// assert_eq!(&[1, 2, 3], &shared[..]);
     /// ```
     #[inline]
-    fn from(mut v: Vec<T>) -> Arc<[T]> {
-        unsafe {
-            let arc = Arc::copy_from_slice(&v);
-
-            // Allow the Vec to free its memory, but not destroy its contents
-            v.set_len(0);
-
-            arc
+    fn from(v: Vec<T>) -> Arc<[T]> {
+        match Arc::try_from_vec_in_place(v) {
+            Ok(rc) => rc,
+            Err(mut v) => {
+                unsafe {
+                    let rc = Arc::copy_from_slice(&v);
+                    // Allow the Vec to free its memory, but not destroy its contents
+                    v.set_len(0);
+                    rc
+                }
+            }
         }
     }
 }
diff --git a/library/alloc/tests/arc.rs b/library/alloc/tests/arc.rs
index ce40b5c9b0a..eb379e4d6a1 100644
--- a/library/alloc/tests/arc.rs
+++ b/library/alloc/tests/arc.rs
@@ -210,3 +210,18 @@ fn weak_may_dangle() {
     // `val` dropped here while still borrowed
     // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::sync::Weak`
 }
+
+#[test]
+fn arc_from_vec_opt() {
+    let mut v = Vec::with_capacity(64);
+    v.push(0usize);
+    let addr = v.as_ptr().cast::<u8>();
+    let arc: Arc<[_]> = v.into();
+    unsafe {
+        assert_eq!(
+            arc.as_ptr().cast::<u8>().offset_from(addr),
+            (std::mem::size_of::<usize>() * 2) as isize,
+            "Vector allocation not reused"
+        );
+    }
+}
diff --git a/library/alloc/tests/rc.rs b/library/alloc/tests/rc.rs
index efb39a60966..1d5f3c52006 100644
--- a/library/alloc/tests/rc.rs
+++ b/library/alloc/tests/rc.rs
@@ -206,3 +206,18 @@ fn weak_may_dangle() {
     // `val` dropped here while still borrowed
     // borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::rc::Weak`
 }
+
+#[test]
+fn rc_from_vec_opt() {
+    let mut v = Vec::with_capacity(64);
+    v.push(0usize);
+    let addr = v.as_ptr().cast::<u8>();
+    let rc: Rc<[_]> = v.into();
+    unsafe {
+        assert_eq!(
+            rc.as_ptr().cast::<u8>().offset_from(addr),
+            (std::mem::size_of::<usize>() * 2) as isize,
+            "Vector allocation not reused"
+        );
+    }
+}
diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs
index e30329aa1cb..9689196ef21 100644
--- a/library/alloc/tests/str.rs
+++ b/library/alloc/tests/str.rs
@@ -1590,11 +1590,27 @@ fn test_bool_from_str() {
     assert_eq!("not even a boolean".parse::<bool>().ok(), None);
 }
 
-fn check_contains_all_substrings(s: &str) {
-    assert!(s.contains(""));
-    for i in 0..s.len() {
-        for j in i + 1..=s.len() {
-            assert!(s.contains(&s[i..j]));
+fn check_contains_all_substrings(haystack: &str) {
+    let mut modified_needle = String::new();
+
+    for i in 0..haystack.len() {
+        // check different haystack lengths since we special-case short haystacks.
+        let haystack = &haystack[0..i];
+        assert!(haystack.contains(""));
+        for j in 0..haystack.len() {
+            for k in j + 1..=haystack.len() {
+                let needle = &haystack[j..k];
+                assert!(haystack.contains(needle));
+                modified_needle.clear();
+                modified_needle.push_str(needle);
+                modified_needle.replace_range(0..1, "\0");
+                assert!(!haystack.contains(&modified_needle));
+
+                modified_needle.clear();
+                modified_needle.push_str(needle);
+                modified_needle.replace_range(needle.len() - 1..needle.len(), "\0");
+                assert!(!haystack.contains(&modified_needle));
+            }
         }
     }
 }
diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs
index 7c5f82f5ea4..f1a51a550f5 100644
--- a/library/core/src/char/convert.rs
+++ b/library/core/src/char/convert.rs
@@ -18,7 +18,6 @@ pub(super) const fn from_u32(i: u32) -> Option<char> {
 }
 
 /// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`].
-#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
 #[inline]
 #[must_use]
 pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char {
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index bb83599369c..c05b68e30bc 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -140,7 +140,7 @@ impl char {
     /// assert_eq!(None, c);
     /// ```
     #[stable(feature = "assoc_char_funcs", since = "1.52.0")]
-    #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
+    #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")]
     #[must_use]
     #[inline]
     pub const fn from_u32(i: u32) -> Option<char> {
@@ -183,7 +183,7 @@ impl char {
     /// assert_eq!('❤', c);
     /// ```
     #[stable(feature = "assoc_char_funcs", since = "1.52.0")]
-    #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
+    #[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")]
     #[must_use]
     #[inline]
     pub const unsafe fn from_u32_unchecked(i: u32) -> char {
@@ -241,7 +241,7 @@ impl char {
     /// let _c = char::from_digit(1, 37);
     /// ```
     #[stable(feature = "assoc_char_funcs", since = "1.52.0")]
-    #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
+    #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")]
     #[must_use]
     #[inline]
     pub const fn from_digit(num: u32, radix: u32) -> Option<char> {
@@ -338,7 +338,7 @@ impl char {
     /// let _ = '1'.to_digit(37);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
+    #[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")]
     #[must_use = "this returns the result of the operation, \
                   without modifying the original"]
     #[inline]
diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs
index b34a7121631..55552376280 100644
--- a/library/core/src/char/mod.rs
+++ b/library/core/src/char/mod.rs
@@ -110,7 +110,7 @@ pub fn decode_utf16<I: IntoIterator<Item = u16>>(iter: I) -> DecodeUtf16<I::Into
 
 /// Converts a `u32` to a `char`. Use [`char::from_u32`] instead.
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
+#[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")]
 #[must_use]
 #[inline]
 pub const fn from_u32(i: u32) -> Option<char> {
@@ -120,7 +120,7 @@ pub const fn from_u32(i: u32) -> Option<char> {
 /// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`].
 /// instead.
 #[stable(feature = "char_from_unchecked", since = "1.5.0")]
-#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
+#[rustc_const_unstable(feature = "const_char_from_u32_unchecked", issue = "89259")]
 #[must_use]
 #[inline]
 pub const unsafe fn from_u32_unchecked(i: u32) -> char {
@@ -130,7 +130,7 @@ pub const unsafe fn from_u32_unchecked(i: u32) -> char {
 
 /// Converts a digit in the given radix to a `char`. Use [`char::from_digit`] instead.
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")]
+#[rustc_const_stable(feature = "const_char_convert", since = "CURRENT_RUSTC_VERSION")]
 #[must_use]
 #[inline]
 pub const fn from_digit(num: u32, radix: u32) -> Option<char> {
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index f0fa2e1d2c1..5db5cbfc3df 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -24,6 +24,7 @@
 
 use crate::const_closure::ConstFnMutClosure;
 use crate::marker::Destruct;
+#[cfg(bootstrap)]
 use crate::marker::StructuralPartialEq;
 
 use self::Ordering::*;
@@ -331,6 +332,7 @@ pub struct AssertParamIsEq<T: Eq + ?Sized> {
 /// assert_eq!(Ordering::Greater, result);
 /// ```
 #[derive(Clone, Copy, Eq, Debug, Hash)]
+#[cfg_attr(not(bootstrap), derive_const(PartialOrd, Ord, PartialEq))]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[repr(i8)]
 pub enum Ordering {
@@ -877,10 +879,12 @@ pub macro Ord($item:item) {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(bootstrap)]
 impl StructuralPartialEq for Ordering {}
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+#[cfg(bootstrap)]
 impl const PartialEq for Ordering {
     #[inline]
     fn eq(&self, other: &Self) -> bool {
@@ -890,6 +894,7 @@ impl const PartialEq for Ordering {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+#[cfg(bootstrap)]
 impl const Ord for Ordering {
     #[inline]
     fn cmp(&self, other: &Ordering) -> Ordering {
@@ -899,6 +904,7 @@ impl const Ord for Ordering {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
+#[cfg(bootstrap)]
 impl const PartialOrd for Ordering {
     #[inline]
     fn partial_cmp(&self, other: &Ordering) -> Option<Ordering> {
diff --git a/library/core/src/default.rs b/library/core/src/default.rs
index a5b4e965552..d96b53de0a3 100644
--- a/library/core/src/default.rs
+++ b/library/core/src/default.rs
@@ -99,7 +99,7 @@
 /// ```
 #[cfg_attr(not(test), rustc_diagnostic_item = "Default")]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(bootstrap), const_trait)]
+#[const_trait]
 pub trait Default: Sized {
     /// Returns the "default value" for a type.
     ///
diff --git a/library/core/src/error.rs b/library/core/src/error.rs
index b24ca037d1a..c9c7cdf4def 100644
--- a/library/core/src/error.rs
+++ b/library/core/src/error.rs
@@ -493,7 +493,7 @@ impl Error for crate::char::ParseCharError {
     }
 }
 
-#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
+#[stable(feature = "duration_checked_float", since = "1.66.0")]
 impl Error for crate::time::TryFromFloatSecsError {}
 
 #[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index c8d28550567..2adc968bd46 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -1054,7 +1054,6 @@ pub trait UpperHex {
 pub trait Pointer {
     /// Formats the value using the given formatter.
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_diagnostic_item = "pointer_trait_fmt"]
     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
 }
 
diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs
index 6487aa08859..107cf92c1c0 100644
--- a/library/core/src/future/mod.rs
+++ b/library/core/src/future/mod.rs
@@ -82,6 +82,7 @@ where
 
     impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> {
         type Output = T::Return;
+        #[track_caller]
         fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
             // SAFETY: Safe because we're !Unpin + !Drop, and this is just a field projection.
             let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };
diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index 3412d3730d0..c53175ba4f3 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -220,7 +220,7 @@ pub fn spin_loop() {
 ///
 /// [`std::convert::identity`]: crate::convert::identity
 #[inline]
-#[stable(feature = "bench_black_box", since = "CURRENT_RUSTC_VERSION")]
+#[stable(feature = "bench_black_box", since = "1.66.0")]
 #[rustc_const_unstable(feature = "const_black_box", issue = "none")]
 pub const fn black_box<T>(dummy: T) -> T {
     crate::intrinsics::black_box(dummy)
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index cec603dcb10..819ccf5a3e9 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -793,7 +793,7 @@ extern "rust-intrinsic" {
     /// uninitialized at that point in the control flow.
     ///
     /// This intrinsic should not be used outside of the compiler.
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn rustc_peek<T>(_: T) -> T;
 
     /// Aborts the execution of the process.
@@ -811,7 +811,7 @@ extern "rust-intrinsic" {
     /// On Unix, the
     /// process will probably terminate with a signal like `SIGABRT`, `SIGILL`, `SIGTRAP`, `SIGSEGV` or
     /// `SIGBUS`.  The precise behaviour is not guaranteed and not stable.
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn abort() -> !;
 
     /// Informs the optimizer that this point in the code is not reachable,
@@ -850,7 +850,7 @@ extern "rust-intrinsic" {
     ///
     /// This intrinsic does not have a stable counterpart.
     #[rustc_const_unstable(feature = "const_likely", issue = "none")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn likely(b: bool) -> bool;
 
     /// Hints to the compiler that branch condition is likely to be false.
@@ -865,7 +865,7 @@ extern "rust-intrinsic" {
     ///
     /// This intrinsic does not have a stable counterpart.
     #[rustc_const_unstable(feature = "const_likely", issue = "none")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn unlikely(b: bool) -> bool;
 
     /// Executes a breakpoint trap, for inspection by a debugger.
@@ -885,7 +885,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is [`core::mem::size_of`].
     #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn size_of<T>() -> usize;
 
     /// The minimum alignment of a type.
@@ -897,7 +897,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is [`core::mem::align_of`].
     #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn min_align_of<T>() -> usize;
     /// The preferred alignment of a type.
     ///
@@ -926,7 +926,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is [`core::any::type_name`].
     #[rustc_const_unstable(feature = "const_type_name", issue = "63084")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn type_name<T: ?Sized>() -> &'static str;
 
     /// Gets an identifier which is globally unique to the specified type. This
@@ -940,7 +940,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is [`core::any::TypeId::of`].
     #[rustc_const_unstable(feature = "const_type_id", issue = "77125")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn type_id<T: ?Sized + 'static>() -> u64;
 
     /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited:
@@ -948,7 +948,7 @@ extern "rust-intrinsic" {
     ///
     /// This intrinsic does not have a stable counterpart.
     #[rustc_const_stable(feature = "const_assert_type", since = "1.59.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn assert_inhabited<T>();
 
     /// A guard for unsafe functions that cannot ever be executed if `T` does not permit
@@ -956,7 +956,7 @@ extern "rust-intrinsic" {
     ///
     /// This intrinsic does not have a stable counterpart.
     #[rustc_const_unstable(feature = "const_assert_type2", issue = "none")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn assert_zero_valid<T>();
 
     /// A guard for unsafe functions that cannot ever be executed if `T` has invalid
@@ -964,7 +964,7 @@ extern "rust-intrinsic" {
     ///
     /// This intrinsic does not have a stable counterpart.
     #[rustc_const_unstable(feature = "const_assert_type2", issue = "none")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn assert_uninit_valid<T>();
 
     /// Gets a reference to a static `Location` indicating where it was called.
@@ -976,7 +976,7 @@ extern "rust-intrinsic" {
     ///
     /// Consider using [`core::panic::Location::caller`] instead.
     #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn caller_location() -> &'static crate::panic::Location<'static>;
 
     /// Moves a value out of scope without running drop glue.
@@ -989,7 +989,7 @@ extern "rust-intrinsic" {
     /// Therefore, implementations must not require the user to uphold
     /// any safety invariants.
     #[rustc_const_unstable(feature = "const_intrinsic_forget", issue = "none")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn forget<T: ?Sized>(_: T);
 
     /// Reinterprets the bits of a value of one type as another type.
@@ -1269,7 +1269,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop).
     #[rustc_const_stable(feature = "const_needs_drop", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn needs_drop<T: ?Sized>() -> bool;
 
     /// Calculates the offset from a pointer.
@@ -1314,7 +1314,7 @@ extern "rust-intrinsic" {
     /// any safety invariants.
     ///
     /// Consider using [`pointer::mask`] instead.
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn ptr_mask<T>(ptr: *const T, mask: usize) -> *const T;
 
     /// Equivalent to the appropriate `llvm.memcpy.p0i8.0i8.*` intrinsic, with
@@ -1506,7 +1506,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is
     /// [`f32::min`]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn minnumf32(x: f32, y: f32) -> f32;
     /// Returns the minimum of two `f64` values.
     ///
@@ -1517,7 +1517,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is
     /// [`f64::min`]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn minnumf64(x: f64, y: f64) -> f64;
     /// Returns the maximum of two `f32` values.
     ///
@@ -1528,7 +1528,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is
     /// [`f32::max`]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn maxnumf32(x: f32, y: f32) -> f32;
     /// Returns the maximum of two `f64` values.
     ///
@@ -1539,7 +1539,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is
     /// [`f64::max`]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn maxnumf64(x: f64, y: f64) -> f64;
 
     /// Copies the sign from `y` to `x` for `f32` values.
@@ -1660,7 +1660,7 @@ extern "rust-intrinsic" {
     /// primitives via the `count_ones` method. For example,
     /// [`u32::count_ones`]
     #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn ctpop<T: Copy>(x: T) -> T;
 
     /// Returns the number of leading unset bits (zeroes) in an integer type `T`.
@@ -1698,7 +1698,7 @@ extern "rust-intrinsic" {
     /// assert_eq!(num_leading, 16);
     /// ```
     #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn ctlz<T: Copy>(x: T) -> T;
 
     /// Like `ctlz`, but extra-unsafe as it returns `undef` when
@@ -1755,7 +1755,7 @@ extern "rust-intrinsic" {
     /// assert_eq!(num_trailing, 16);
     /// ```
     #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn cttz<T: Copy>(x: T) -> T;
 
     /// Like `cttz`, but extra-unsafe as it returns `undef` when
@@ -1788,7 +1788,7 @@ extern "rust-intrinsic" {
     /// primitives via the `swap_bytes` method. For example,
     /// [`u32::swap_bytes`]
     #[rustc_const_stable(feature = "const_bswap", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn bswap<T: Copy>(x: T) -> T;
 
     /// Reverses the bits in an integer type `T`.
@@ -1802,7 +1802,7 @@ extern "rust-intrinsic" {
     /// primitives via the `reverse_bits` method. For example,
     /// [`u32::reverse_bits`]
     #[rustc_const_stable(feature = "const_bitreverse", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn bitreverse<T: Copy>(x: T) -> T;
 
     /// Performs checked integer addition.
@@ -1816,7 +1816,7 @@ extern "rust-intrinsic" {
     /// primitives via the `overflowing_add` method. For example,
     /// [`u32::overflowing_add`]
     #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn add_with_overflow<T: Copy>(x: T, y: T) -> (T, bool);
 
     /// Performs checked integer subtraction
@@ -1830,7 +1830,7 @@ extern "rust-intrinsic" {
     /// primitives via the `overflowing_sub` method. For example,
     /// [`u32::overflowing_sub`]
     #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn sub_with_overflow<T: Copy>(x: T, y: T) -> (T, bool);
 
     /// Performs checked integer multiplication
@@ -1844,7 +1844,7 @@ extern "rust-intrinsic" {
     /// primitives via the `overflowing_mul` method. For example,
     /// [`u32::overflowing_mul`]
     #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn mul_with_overflow<T: Copy>(x: T, y: T) -> (T, bool);
 
     /// Performs an exact division, resulting in undefined behavior where
@@ -1919,7 +1919,7 @@ extern "rust-intrinsic" {
     /// primitives via the `rotate_left` method. For example,
     /// [`u32::rotate_left`]
     #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
 
     /// Performs rotate right.
@@ -1933,7 +1933,7 @@ extern "rust-intrinsic" {
     /// primitives via the `rotate_right` method. For example,
     /// [`u32::rotate_right`]
     #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
 
     /// Returns (a + b) mod 2<sup>N</sup>, where N is the width of T in bits.
@@ -1947,7 +1947,7 @@ extern "rust-intrinsic" {
     /// primitives via the `wrapping_add` method. For example,
     /// [`u32::wrapping_add`]
     #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn wrapping_add<T: Copy>(a: T, b: T) -> T;
     /// Returns (a - b) mod 2<sup>N</sup>, where N is the width of T in bits.
     ///
@@ -1960,7 +1960,7 @@ extern "rust-intrinsic" {
     /// primitives via the `wrapping_sub` method. For example,
     /// [`u32::wrapping_sub`]
     #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn wrapping_sub<T: Copy>(a: T, b: T) -> T;
     /// Returns (a * b) mod 2<sup>N</sup>, where N is the width of T in bits.
     ///
@@ -1973,7 +1973,7 @@ extern "rust-intrinsic" {
     /// primitives via the `wrapping_mul` method. For example,
     /// [`u32::wrapping_mul`]
     #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn wrapping_mul<T: Copy>(a: T, b: T) -> T;
 
     /// Computes `a + b`, saturating at numeric bounds.
@@ -1987,7 +1987,7 @@ extern "rust-intrinsic" {
     /// primitives via the `saturating_add` method. For example,
     /// [`u32::saturating_add`]
     #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn saturating_add<T: Copy>(a: T, b: T) -> T;
     /// Computes `a - b`, saturating at numeric bounds.
     ///
@@ -2000,7 +2000,7 @@ extern "rust-intrinsic" {
     /// primitives via the `saturating_sub` method. For example,
     /// [`u32::saturating_sub`]
     #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn saturating_sub<T: Copy>(a: T, b: T) -> T;
 
     /// Returns the value of the discriminant for the variant in 'v';
@@ -2013,7 +2013,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is [`core::mem::discriminant`].
     #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
 
     /// Returns the number of variants of the type `T` cast to a `usize`;
@@ -2026,7 +2026,7 @@ extern "rust-intrinsic" {
     ///
     /// The to-be-stabilized version of this intrinsic is [`mem::variant_count`].
     #[rustc_const_unstable(feature = "variant_count", issue = "73662")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn variant_count<T>() -> usize;
 
     /// Rust's "try catch" construct which invokes the function pointer `try_fn`
@@ -2060,7 +2060,7 @@ extern "rust-intrinsic" {
     /// Therefore, implementations must not require the user to uphold
     /// any safety invariants.
     #[rustc_const_unstable(feature = "const_raw_ptr_comparison", issue = "53020")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn ptr_guaranteed_cmp<T>(ptr: *const T, other: *const T) -> u8;
 
     /// Allocates a block of memory at compile time.
@@ -2111,7 +2111,7 @@ extern "rust-intrinsic" {
     ///
     /// [`std::hint::black_box`]: crate::hint::black_box
     #[rustc_const_unstable(feature = "const_black_box", issue = "none")]
-    #[cfg_attr(not(bootstrap), rustc_safe_intrinsic)]
+    #[rustc_safe_intrinsic]
     pub fn black_box<T>(dummy: T) -> T;
 
     /// `ptr` must point to a vtable.
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 5dc7427bee0..2d12805270f 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -104,7 +104,7 @@
 #![feature(const_black_box)]
 #![feature(const_caller_location)]
 #![feature(const_cell_into_inner)]
-#![feature(const_char_convert)]
+#![feature(const_char_from_u32_unchecked)]
 #![feature(const_clone)]
 #![feature(const_cmp)]
 #![feature(const_discriminant)]
@@ -185,6 +185,7 @@
 #![feature(const_refs_to_cell)]
 #![feature(decl_macro)]
 #![feature(deprecated_suggestion)]
+#![cfg_attr(not(bootstrap), feature(derive_const))]
 #![feature(doc_cfg)]
 #![feature(doc_notable_trait)]
 #![feature(rustdoc_internals)]
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 32bdc26bc51..34247c05845 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -338,7 +338,6 @@ pub macro debug_assert_matches($($arg:tt)*) {
 /// ```
 #[macro_export]
 #[stable(feature = "matches_macro", since = "1.42.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "matches_macro")]
 macro_rules! matches {
     ($expression:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => {
         match $expression {
@@ -820,7 +819,6 @@ pub(crate) mod builtin {
     #[stable(feature = "compile_error_macro", since = "1.20.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "compile_error_macro")]
     macro_rules! compile_error {
         ($msg:expr $(,)?) => {{ /* compiler built-in */ }};
     }
@@ -944,7 +942,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "env_macro")]
     macro_rules! env {
         ($name:expr $(,)?) => {{ /* compiler built-in */ }};
         ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }};
@@ -973,7 +970,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "option_env_macro")]
     macro_rules! option_env {
         ($name:expr $(,)?) => {{ /* compiler built-in */ }};
     }
@@ -1058,7 +1054,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "concat_macro")]
     macro_rules! concat {
         ($($e:expr),* $(,)?) => {{ /* compiler built-in */ }};
     }
@@ -1084,7 +1079,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "line_macro")]
     macro_rules! line {
         () => {
             /* compiler built-in */
@@ -1124,7 +1118,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "column_macro")]
     macro_rules! column {
         () => {
             /* compiler built-in */
@@ -1150,7 +1143,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "file_macro")]
     macro_rules! file {
         () => {
             /* compiler built-in */
@@ -1175,7 +1167,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "stringify_macro")]
     macro_rules! stringify {
         ($($t:tt)*) => {
             /* compiler built-in */
@@ -1282,7 +1273,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "module_path_macro")]
     macro_rules! module_path {
         () => {
             /* compiler built-in */
@@ -1316,7 +1306,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "cfg_macro")]
     macro_rules! cfg {
         ($($cfg:tt)*) => {
             /* compiler built-in */
@@ -1367,7 +1356,6 @@ pub(crate) mod builtin {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_builtin_macro]
     #[macro_export]
-    #[cfg_attr(not(test), rustc_diagnostic_item = "include_macro")]
     macro_rules! include {
         ($file:expr $(,)?) => {{ /* compiler built-in */ }};
     }
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index ae4ebf44442..3eff6033f8d 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -96,6 +96,7 @@ unsafe impl<T: Sync + ?Sized> Send for &T {}
 )]
 #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable
 #[rustc_specialization_trait]
+#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
 pub trait Sized {
     // Empty.
 }
@@ -127,6 +128,7 @@ pub trait Sized {
 /// [nomicon-coerce]: ../../nomicon/coercions.html
 #[unstable(feature = "unsize", issue = "27732")]
 #[lang = "unsize"]
+#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
 pub trait Unsize<T: ?Sized> {
     // Empty.
 }
@@ -693,6 +695,7 @@ impl<T: ?Sized> StructuralEq for PhantomData<T> {}
     reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead"
 )]
 #[lang = "discriminant_kind"]
+#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
 pub trait DiscriminantKind {
     /// The type of the discriminant, which must satisfy the trait
     /// bounds required by `mem::Discriminant`.
@@ -793,6 +796,7 @@ impl<T: ?Sized> Unpin for *mut T {}
 #[lang = "destruct"]
 #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)]
 #[const_trait]
+#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
 pub trait Destruct {}
 
 /// A marker for tuple types.
@@ -802,6 +806,7 @@ pub trait Destruct {}
 #[unstable(feature = "tuple_trait", issue = "none")]
 #[lang = "tuple_trait"]
 #[rustc_on_unimplemented(message = "`{Self}` is not a tuple")]
+#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
 pub trait Tuple {}
 
 /// Implementations of `Copy` for primitive types.
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
index 7757c95de9d..3f491836551 100644
--- a/library/core/src/mem/maybe_uninit.rs
+++ b/library/core/src/mem/maybe_uninit.rs
@@ -1172,7 +1172,7 @@ impl<T> MaybeUninit<T> {
     /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)]
     /// use std::mem::MaybeUninit;
     ///
-    /// let val = 0x12345678i32;
+    /// let val = 0x12345678_i32;
     /// let uninit = MaybeUninit::new(val);
     /// let uninit_bytes = uninit.as_bytes();
     /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) };
@@ -1198,7 +1198,7 @@ impl<T> MaybeUninit<T> {
     /// #![feature(maybe_uninit_as_bytes)]
     /// use std::mem::MaybeUninit;
     ///
-    /// let val = 0x12345678i32;
+    /// let val = 0x12345678_i32;
     /// let mut uninit = MaybeUninit::new(val);
     /// let uninit_bytes = uninit.as_bytes_mut();
     /// if cfg!(target_endian = "little") {
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 956a69eda8a..ec35914f1e3 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -725,10 +725,7 @@ pub const fn swap<T>(x: &mut T, y: &mut T) {
     // understanding `mem::replace`, `Option::take`, etc. - a better overall
     // solution might be to make `ptr::swap_nonoverlapping` into an intrinsic, which
     // a backend can choose to implement using the block optimization, or not.
-    // NOTE(scottmcm) MIRI is disabled here as reading in smaller units is a
-    // pessimization for it.  Also, if the type contains any unaligned pointers,
-    // copying those over multiple reads is difficult to support.
-    #[cfg(not(any(target_arch = "spirv", miri)))]
+    #[cfg(not(any(target_arch = "spirv")))]
     {
         // For types that are larger multiples of their alignment, the simple way
         // tends to copy the whole thing to stack rather than doing it one part
diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs
index 8ced5971ec2..71b14d0ae3f 100644
--- a/library/core/src/num/flt2dec/strategy/dragon.rs
+++ b/library/core/src/num/flt2dec/strategy/dragon.rs
@@ -366,7 +366,7 @@ pub fn format_exact<'a>(
     if order == Ordering::Greater
         || (order == Ordering::Equal
             // SAFETY: `buf[len-1]` is initialized.
-            && (len == 0 || unsafe { buf[len - 1].assume_init() } & 1 == 1))
+            && len > 0 && unsafe { buf[len - 1].assume_init() } & 1 == 1)
     {
         // if rounding up changes the length, the exponent should also change.
         // but we've been requested a fixed number of digits, so do not alter the buffer...
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 582c8524cf0..e64eb1cf7ae 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -471,8 +471,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_add_unsigned(2), Some(3));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add_unsigned(3), None);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -539,8 +539,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_sub_unsigned(2), Some(-1));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).checked_sub_unsigned(3), None);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -909,8 +909,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_add_unsigned(2), 3);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_add_unsigned(100), ", stringify!($SelfT), "::MAX);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -955,8 +955,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".saturating_sub_unsigned(127), -27);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_sub_unsigned(100), ", stringify!($SelfT), "::MIN);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -1133,8 +1133,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".wrapping_add_unsigned(27), 127);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_add_unsigned(2), ", stringify!($SelfT), "::MIN + 1);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
@@ -1173,8 +1173,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".wrapping_sub_unsigned(127), -127);")]
         #[doc = concat!("assert_eq!((-2", stringify!($SelfT), ").wrapping_sub_unsigned(", stringify!($UnsignedT), "::MAX), -1);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
@@ -1570,8 +1570,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN).overflowing_add_unsigned(", stringify!($UnsignedT), "::MAX), (", stringify!($SelfT), "::MAX, false));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).overflowing_add_unsigned(3), (", stringify!($SelfT), "::MIN, true));")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -1652,8 +1652,8 @@ macro_rules! int_impl {
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX).overflowing_sub_unsigned(", stringify!($UnsignedT), "::MAX), (", stringify!($SelfT), "::MIN, false));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).overflowing_sub_unsigned(3), (", stringify!($SelfT), "::MAX, true));")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index 5b7521220ac..6a417b54daa 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -321,7 +321,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
@@ -356,7 +355,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
@@ -391,8 +389,8 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 /// #![feature(nonzero_ops)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let one = ", stringify!($Ty), "::new(1)?;")]
@@ -420,7 +418,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -461,7 +458,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(7).unwrap().ilog2(), 2);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(8).unwrap().ilog2(), 3);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(9).unwrap().ilog2(), 3);")]
@@ -486,7 +482,6 @@ macro_rules! nonzero_unsigned_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(99).unwrap().ilog10(), 1);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(100).unwrap().ilog10(), 2);")]
                 #[doc = concat!("assert_eq!(", stringify!($Ty), "::new(101).unwrap().ilog10(), 2);")]
@@ -526,7 +521,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -556,7 +550,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -591,7 +584,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -626,7 +618,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -662,7 +653,6 @@ macro_rules! nonzero_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let pos = ", stringify!($Ty), "::new(1)?;")]
@@ -905,7 +895,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -941,7 +930,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -986,8 +974,8 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 /// #![feature(nonzero_ops)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let two = ", stringify!($Ty), "::new(2)?;")]
@@ -1014,7 +1002,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let three = ", stringify!($Ty), "::new(3)?;")]
@@ -1058,7 +1045,6 @@ macro_rules! nonzero_unsigned_signed_operations {
                 ///
                 /// ```
                 #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
-                ///
                 /// # fn main() { test().unwrap(); }
                 /// # fn test() -> Option<()> {
                 #[doc = concat!("let three = ", stringify!($Ty), "::new(3)?;")]
@@ -1162,8 +1148,8 @@ macro_rules! nonzero_min_max_unsigned {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[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")]
@@ -1177,8 +1163,8 @@ macro_rules! nonzero_min_max_unsigned {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[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")]
@@ -1204,8 +1190,8 @@ macro_rules! nonzero_min_max_signed {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[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")]
@@ -1223,8 +1209,8 @@ macro_rules! nonzero_min_max_signed {
                 ///
                 /// ```
                 /// #![feature(nonzero_min_max)]
-                #[doc = concat!("# use std::num::", stringify!($Ty), ";")]
                 ///
+                #[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")]
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index d3ba79a2e64..741d7ec6f59 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -482,8 +482,8 @@ macro_rules! uint_impl {
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".checked_add_signed(-2), None);")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add_signed(3), None);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -1032,8 +1032,8 @@ macro_rules! uint_impl {
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".saturating_add_signed(-2), 0);")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).saturating_add_signed(4), ", stringify!($SelfT), "::MAX);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -1172,8 +1172,8 @@ macro_rules! uint_impl {
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_add_signed(-2), ", stringify!($SelfT), "::MAX);")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).wrapping_add_signed(4), 1);")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -1541,8 +1541,8 @@ macro_rules! uint_impl {
         #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".overflowing_add_signed(-2), (", stringify!($SelfT), "::MAX, true));")]
         #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).overflowing_add_signed(4), (1, true));")]
         /// ```
-        #[stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
-        #[rustc_const_stable(feature = "mixed_integer_ops", since = "CURRENT_RUSTC_VERSION")]
+        #[stable(feature = "mixed_integer_ops", since = "1.66.0")]
+        #[rustc_const_stable(feature = "mixed_integer_ops", since = "1.66.0")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs
index 4f4c99c4ad9..c67867f4436 100644
--- a/library/core/src/ops/deref.rs
+++ b/library/core/src/ops/deref.rs
@@ -61,7 +61,7 @@
 #[doc(alias = "&*")]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_diagnostic_item = "Deref"]
-#[cfg_attr(not(bootstrap), const_trait)]
+#[const_trait]
 pub trait Deref {
     /// The resulting type after dereferencing.
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs
index 11b43b621c7..127b047db91 100644
--- a/library/core/src/ops/function.rs
+++ b/library/core/src/ops/function.rs
@@ -57,7 +57,6 @@ use crate::marker::Tuple;
 #[cfg(bootstrap)]
 #[lang = "fn"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "Fn"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -75,6 +74,7 @@ use crate::marker::Tuple;
 )]
 #[fundamental] // so that regex can rely that `&str: !FnMut`
 #[must_use = "closures are lazy and do nothing unless called"]
+#[const_trait]
 pub trait Fn<Args>: FnMut<Args> {
     /// Performs the call operation.
     #[unstable(feature = "fn_traits", issue = "29625")]
@@ -137,7 +137,6 @@ pub trait Fn<Args>: FnMut<Args> {
 #[cfg(not(bootstrap))]
 #[lang = "fn"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "Fn"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -226,7 +225,6 @@ pub trait Fn<Args: Tuple>: FnMut<Args> {
 #[cfg(bootstrap)]
 #[lang = "fn_mut"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnMut"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -244,6 +242,7 @@ pub trait Fn<Args: Tuple>: FnMut<Args> {
 )]
 #[fundamental] // so that regex can rely that `&str: !FnMut`
 #[must_use = "closures are lazy and do nothing unless called"]
+#[const_trait]
 pub trait FnMut<Args>: FnOnce<Args> {
     /// Performs the call operation.
     #[unstable(feature = "fn_traits", issue = "29625")]
@@ -314,7 +313,6 @@ pub trait FnMut<Args>: FnOnce<Args> {
 #[cfg(not(bootstrap))]
 #[lang = "fn_mut"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnMut"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -395,7 +393,6 @@ pub trait FnMut<Args: Tuple>: FnOnce<Args> {
 #[cfg(bootstrap)]
 #[lang = "fn_once"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnOnce"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -413,6 +410,7 @@ pub trait FnMut<Args: Tuple>: FnOnce<Args> {
 )]
 #[fundamental] // so that regex can rely that `&str: !FnMut`
 #[must_use = "closures are lazy and do nothing unless called"]
+#[const_trait]
 pub trait FnOnce<Args> {
     /// The returned type after the call operator is used.
     #[lang = "fn_once_output"]
@@ -480,7 +478,6 @@ pub trait FnOnce<Args> {
 #[cfg(not(bootstrap))]
 #[lang = "fn_once"]
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_diagnostic_item = "FnOnce"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs
index dd4e3ac1c2f..5e3dc48b6ca 100644
--- a/library/core/src/ops/index.rs
+++ b/library/core/src/ops/index.rs
@@ -55,7 +55,7 @@
 #[doc(alias = "]")]
 #[doc(alias = "[")]
 #[doc(alias = "[]")]
-#[cfg_attr(not(bootstrap), const_trait)]
+#[const_trait]
 pub trait Index<Idx: ?Sized> {
     /// The returned type after indexing.
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -164,7 +164,7 @@ see chapter in The Book <https://doc.rust-lang.org/book/ch08-02-strings.html#ind
 #[doc(alias = "[")]
 #[doc(alias = "]")]
 #[doc(alias = "[]")]
-#[cfg_attr(not(bootstrap), const_trait)]
+#[const_trait]
 pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
     /// Performs the mutable indexing (`container[index]`) operation.
     ///
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index a81dbc6924f..f284b435955 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -1720,7 +1720,7 @@ impl<T, U> Option<(T, U)> {
     /// assert_eq!(y.unzip(), (None, None));
     /// ```
     #[inline]
-    #[stable(feature = "unzip_option", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "unzip_option", since = "1.66.0")]
     #[rustc_const_unstable(feature = "const_option", issue = "67441")]
     pub const fn unzip(self) -> (Option<T>, Option<U>)
     where
diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs
index 00b63dfbd06..461b70c32f3 100644
--- a/library/core/src/panic.rs
+++ b/library/core/src/panic.rs
@@ -80,7 +80,6 @@ pub macro unreachable_2015 {
 #[doc(hidden)]
 #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
 #[allow_internal_unstable(core_panic)]
-#[rustc_diagnostic_item = "unreachable_2021_macro"]
 #[rustc_macro_transparency = "semitransparent"]
 pub macro unreachable_2021 {
     () => (
diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs
index a9de7c94e5a..4fd1eb23413 100644
--- a/library/core/src/panicking.rs
+++ b/library/core/src/panicking.rs
@@ -70,8 +70,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
 #[cold]
 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
 #[cfg_attr(feature = "panic_immediate_abort", inline)]
-#[cfg_attr(not(bootstrap), rustc_nounwind)]
-#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
+#[rustc_nounwind]
 pub fn panic_str_nounwind(msg: &'static str) -> ! {
     if cfg!(feature = "panic_immediate_abort") {
         super::intrinsics::abort()
@@ -158,8 +157,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
 #[cold]
 #[inline(never)]
 #[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
-#[cfg_attr(not(bootstrap), rustc_nounwind)]
-#[cfg_attr(bootstrap, rustc_allocator_nounwind)]
+#[rustc_nounwind]
 fn panic_no_unwind() -> ! {
     panic_str_nounwind("panic in a function that cannot unwind")
 }
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 926d2ae5113..e28ddf3c75e 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -709,7 +709,7 @@ impl<T: ?Sized> *const T {
     #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
     #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
     #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
-    pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize {
+    pub const unsafe fn byte_offset_from<U: ?Sized>(self, origin: *const U) -> isize {
         // SAFETY: the caller must uphold the safety contract for `offset_from`.
         unsafe { self.cast::<u8>().offset_from(origin.cast::<u8>()) }
     }
diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs
index caa10f1818b..a8604843e96 100644
--- a/library/core/src/ptr/metadata.rs
+++ b/library/core/src/ptr/metadata.rs
@@ -50,6 +50,7 @@ use crate::hash::{Hash, Hasher};
 ///
 /// [`to_raw_parts`]: *const::to_raw_parts
 #[lang = "pointee_trait"]
+#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
 pub trait Pointee {
     /// The type for metadata in pointers and references to `Self`.
     #[lang = "metadata_type"]
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 565c38d222a..9d4bae3eaa5 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -35,7 +35,8 @@
 //!   be used for inter-thread synchronization.
 //! * The result of casting a reference to a pointer is valid for as long as the
 //!   underlying object is live and no reference (just raw pointers) is used to
-//!   access the same memory.
+//!   access the same memory. That is, reference and pointer accesses cannot be
+//!   interleaved.
 //!
 //! These axioms, along with careful use of [`offset`] for pointer arithmetic,
 //! are enough to correctly implement many useful things in unsafe code. Stronger guarantees
@@ -64,7 +65,6 @@
 //! separate allocated object), heap allocations (each allocation created by the global allocator is
 //! a separate allocated object), and `static` variables.
 //!
-//!
 //! # Strict Provenance
 //!
 //! **The following text is non-normative, insufficiently formal, and is an extremely strict
@@ -908,21 +908,15 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
         );
     }
 
-    // NOTE(scottmcm) Miri is disabled here as reading in smaller units is a
-    // pessimization for it.  Also, if the type contains any unaligned pointers,
-    // copying those over multiple reads is difficult to support.
-    #[cfg(not(miri))]
+    // Split up the slice into small power-of-two-sized chunks that LLVM is able
+    // to vectorize (unless it's a special type with more-than-pointer alignment,
+    // because we don't want to pessimize things like slices of SIMD vectors.)
+    if mem::align_of::<T>() <= mem::size_of::<usize>()
+        && (!mem::size_of::<T>().is_power_of_two()
+            || mem::size_of::<T>() > mem::size_of::<usize>() * 2)
     {
-        // Split up the slice into small power-of-two-sized chunks that LLVM is able
-        // to vectorize (unless it's a special type with more-than-pointer alignment,
-        // because we don't want to pessimize things like slices of SIMD vectors.)
-        if mem::align_of::<T>() <= mem::size_of::<usize>()
-            && (!mem::size_of::<T>().is_power_of_two()
-                || mem::size_of::<T>() > mem::size_of::<usize>() * 2)
-        {
-            attempt_swap_as_chunks!(usize);
-            attempt_swap_as_chunks!(u8);
-        }
+        attempt_swap_as_chunks!(usize);
+        attempt_swap_as_chunks!(u8);
     }
 
     // SAFETY: Same preconditions as this function
@@ -1862,7 +1856,6 @@ macro_rules! fnptr_impls_safety_abi {
         fnptr_impls_safety_abi! { #[stable(feature = "fnptr_impls", since = "1.4.0")] $FnTy, $($Arg),* }
     };
     (@c_unwind $FnTy: ty, $($Arg: ident),*) => {
-        #[cfg(not(bootstrap))]
         fnptr_impls_safety_abi! { #[unstable(feature = "c_unwind", issue = "74990")] $FnTy, $($Arg),* }
     };
     (#[$meta:meta] $FnTy: ty, $($Arg: ident),*) => {
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index f71696e9ca0..ba21126dbd2 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -889,7 +889,7 @@ impl<T: ?Sized> *mut T {
     #[unstable(feature = "pointer_byte_offsets", issue = "96283")]
     #[rustc_const_unstable(feature = "const_pointer_byte_offsets", issue = "96283")]
     #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
-    pub const unsafe fn byte_offset_from(self, origin: *const T) -> isize {
+    pub const unsafe fn byte_offset_from<U: ?Sized>(self, origin: *const U) -> isize {
         // SAFETY: the caller must uphold the safety contract for `offset_from`.
         unsafe { self.cast::<u8>().offset_from(origin.cast::<u8>()) }
     }
diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs
index ec2cb429e67..c5be32861f9 100644
--- a/library/core/src/str/pattern.rs
+++ b/library/core/src/str/pattern.rs
@@ -39,6 +39,7 @@
 )]
 
 use crate::cmp;
+use crate::cmp::Ordering;
 use crate::fmt;
 use crate::slice::memchr;
 
@@ -946,6 +947,32 @@ impl<'a, 'b> Pattern<'a> for &'b str {
         haystack.as_bytes().starts_with(self.as_bytes())
     }
 
+    /// Checks whether the pattern matches anywhere in the haystack
+    #[inline]
+    fn is_contained_in(self, haystack: &'a str) -> bool {
+        if self.len() == 0 {
+            return true;
+        }
+
+        match self.len().cmp(&haystack.len()) {
+            Ordering::Less => {
+                if self.len() == 1 {
+                    return haystack.as_bytes().contains(&self.as_bytes()[0]);
+                }
+
+                #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
+                if self.len() <= 32 {
+                    if let Some(result) = simd_contains(self, haystack) {
+                        return result;
+                    }
+                }
+
+                self.into_searcher(haystack).next_match().is_some()
+            }
+            _ => self == haystack,
+        }
+    }
+
     /// Removes the pattern from the front of haystack, if it matches.
     #[inline]
     fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> {
@@ -1684,3 +1711,208 @@ impl TwoWayStrategy for RejectAndMatch {
         SearchStep::Match(a, b)
     }
 }
+
+/// SIMD search for short needles based on
+/// Wojciech Muła's "SIMD-friendly algorithms for substring searching"[0]
+///
+/// It skips ahead by the vector width on each iteration (rather than the needle length as two-way
+/// does) by probing the first and last byte of the needle for the whole vector width
+/// and only doing full needle comparisons when the vectorized probe indicated potential matches.
+///
+/// Since the x86_64 baseline only offers SSE2 we only use u8x16 here.
+/// If we ever ship std with for x86-64-v3 or adapt this for other platforms then wider vectors
+/// should be evaluated.
+///
+/// For haystacks smaller than vector-size + needle length it falls back to
+/// a naive O(n*m) search so this implementation should not be called on larger needles.
+///
+/// [0]: http://0x80.pl/articles/simd-strfind.html#sse-avx2
+#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
+#[inline]
+fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
+    let needle = needle.as_bytes();
+    let haystack = haystack.as_bytes();
+
+    debug_assert!(needle.len() > 1);
+
+    use crate::ops::BitAnd;
+    use crate::simd::mask8x16 as Mask;
+    use crate::simd::u8x16 as Block;
+    use crate::simd::{SimdPartialEq, ToBitMask};
+
+    let first_probe = needle[0];
+
+    // the offset used for the 2nd vector
+    let second_probe_offset = if needle.len() == 2 {
+        // never bail out on len=2 needles because the probes will fully cover them and have
+        // no degenerate cases.
+        1
+    } else {
+        // try a few bytes in case first and last byte of the needle are the same
+        let Some(second_probe_offset) = (needle.len().saturating_sub(4)..needle.len()).rfind(|&idx| needle[idx] != first_probe) else {
+            // fall back to other search methods if we can't find any different bytes
+            // since we could otherwise hit some degenerate cases
+            return None;
+        };
+        second_probe_offset
+    };
+
+    // do a naive search if the haystack is too small to fit
+    if haystack.len() < Block::LANES + second_probe_offset {
+        return Some(haystack.windows(needle.len()).any(|c| c == needle));
+    }
+
+    let first_probe: Block = Block::splat(first_probe);
+    let second_probe: Block = Block::splat(needle[second_probe_offset]);
+    // first byte are already checked by the outer loop. to verify a match only the
+    // remainder has to be compared.
+    let trimmed_needle = &needle[1..];
+
+    // this #[cold] is load-bearing, benchmark before removing it...
+    let check_mask = #[cold]
+    |idx, mask: u16, skip: bool| -> bool {
+        if skip {
+            return false;
+        }
+
+        // and so is this. optimizations are weird.
+        let mut mask = mask;
+
+        while mask != 0 {
+            let trailing = mask.trailing_zeros();
+            let offset = idx + trailing as usize + 1;
+            // SAFETY: mask is between 0 and 15 trailing zeroes, we skip one additional byte that was already compared
+            // and then take trimmed_needle.len() bytes. This is within the bounds defined by the outer loop
+            unsafe {
+                let sub = haystack.get_unchecked(offset..).get_unchecked(..trimmed_needle.len());
+                if small_slice_eq(sub, trimmed_needle) {
+                    return true;
+                }
+            }
+            mask &= !(1 << trailing);
+        }
+        return false;
+    };
+
+    let test_chunk = |idx| -> u16 {
+        // SAFETY: this requires at least LANES bytes being readable at idx
+        // that is ensured by the loop ranges (see comments below)
+        let a: Block = unsafe { haystack.as_ptr().add(idx).cast::<Block>().read_unaligned() };
+        // SAFETY: this requires LANES + block_offset bytes being readable at idx
+        let b: Block = unsafe {
+            haystack.as_ptr().add(idx).add(second_probe_offset).cast::<Block>().read_unaligned()
+        };
+        let eq_first: Mask = a.simd_eq(first_probe);
+        let eq_last: Mask = b.simd_eq(second_probe);
+        let both = eq_first.bitand(eq_last);
+        let mask = both.to_bitmask();
+
+        return mask;
+    };
+
+    let mut i = 0;
+    let mut result = false;
+    // The loop condition must ensure that there's enough headroom to read LANE bytes,
+    // and not only at the current index but also at the index shifted by block_offset
+    const UNROLL: usize = 4;
+    while i + second_probe_offset + UNROLL * Block::LANES < haystack.len() && !result {
+        let mut masks = [0u16; UNROLL];
+        for j in 0..UNROLL {
+            masks[j] = test_chunk(i + j * Block::LANES);
+        }
+        for j in 0..UNROLL {
+            let mask = masks[j];
+            if mask != 0 {
+                result |= check_mask(i + j * Block::LANES, mask, result);
+            }
+        }
+        i += UNROLL * Block::LANES;
+    }
+    while i + second_probe_offset + Block::LANES < haystack.len() && !result {
+        let mask = test_chunk(i);
+        if mask != 0 {
+            result |= check_mask(i, mask, result);
+        }
+        i += Block::LANES;
+    }
+
+    // Process the tail that didn't fit into LANES-sized steps.
+    // This simply repeats the same procedure but as right-aligned chunk instead
+    // of a left-aligned one. The last byte must be exactly flush with the string end so
+    // we don't miss a single byte or read out of bounds.
+    let i = haystack.len() - second_probe_offset - Block::LANES;
+    let mask = test_chunk(i);
+    if mask != 0 {
+        result |= check_mask(i, mask, result);
+    }
+
+    Some(result)
+}
+
+/// Compares short slices for equality.
+///
+/// It avoids a call to libc's memcmp which is faster on long slices
+/// due to SIMD optimizations but it incurs a function call overhead.
+///
+/// # Safety
+///
+/// Both slices must have the same length.
+#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] // only called on x86
+#[inline]
+unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
+    // This function is adapted from
+    // https://github.com/BurntSushi/memchr/blob/8037d11b4357b0f07be2bb66dc2659d9cf28ad32/src/memmem/util.rs#L32
+
+    // If we don't have enough bytes to do 4-byte at a time loads, then
+    // fall back to the naive slow version.
+    //
+    // Potential alternative: We could do a copy_nonoverlapping combined with a mask instead
+    // of a loop. Benchmark it.
+    if x.len() < 4 {
+        for (&b1, &b2) in x.iter().zip(y) {
+            if b1 != b2 {
+                return false;
+            }
+        }
+        return true;
+    }
+    // When we have 4 or more bytes to compare, then proceed in chunks of 4 at
+    // a time using unaligned loads.
+    //
+    // Also, why do 4 byte loads instead of, say, 8 byte loads? The reason is
+    // that this particular version of memcmp is likely to be called with tiny
+    // needles. That means that if we do 8 byte loads, then a higher proportion
+    // of memcmp calls will use the slower variant above. With that said, this
+    // is a hypothesis and is only loosely supported by benchmarks. There's
+    // likely some improvement that could be made here. The main thing here
+    // though is to optimize for latency, not throughput.
+
+    // SAFETY: Via the conditional above, we know that both `px` and `py`
+    // have the same length, so `px < pxend` implies that `py < pyend`.
+    // Thus, derefencing both `px` and `py` in the loop below is safe.
+    //
+    // Moreover, we set `pxend` and `pyend` to be 4 bytes before the actual
+    // end of of `px` and `py`. Thus, the final dereference outside of the
+    // loop is guaranteed to be valid. (The final comparison will overlap with
+    // the last comparison done in the loop for lengths that aren't multiples
+    // of four.)
+    //
+    // Finally, we needn't worry about alignment here, since we do unaligned
+    // loads.
+    unsafe {
+        let (mut px, mut py) = (x.as_ptr(), y.as_ptr());
+        let (pxend, pyend) = (px.add(x.len() - 4), py.add(y.len() - 4));
+        while px < pxend {
+            let vx = (px as *const u32).read_unaligned();
+            let vy = (py as *const u32).read_unaligned();
+            if vx != vy {
+                return false;
+            }
+            px = px.add(4);
+            py = py.add(4);
+        }
+        let vx = (pxend as *const u32).read_unaligned();
+        let vy = (pyend as *const u32).read_unaligned();
+        vx == vy
+    }
+}
diff --git a/library/core/src/time.rs b/library/core/src/time.rs
index 37c3611d0a9..ba1cb6efa04 100644
--- a/library/core/src/time.rs
+++ b/library/core/src/time.rs
@@ -1232,7 +1232,7 @@ impl fmt::Debug for Duration {
 /// }
 /// ```
 #[derive(Debug, Clone, PartialEq, Eq)]
-#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
+#[stable(feature = "duration_checked_float", since = "1.66.0")]
 pub struct TryFromFloatSecsError {
     kind: TryFromFloatSecsErrorKind,
 }
@@ -1250,7 +1250,7 @@ impl TryFromFloatSecsError {
     }
 }
 
-#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
+#[stable(feature = "duration_checked_float", since = "1.66.0")]
 impl fmt::Display for TryFromFloatSecsError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.description().fmt(f)
@@ -1401,7 +1401,7 @@ impl Duration {
     /// let res = Duration::try_from_secs_f32(val);
     /// assert_eq!(res, Ok(Duration::new(1, 2_929_688)));
     /// ```
-    #[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "duration_checked_float", since = "1.66.0")]
     #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
     #[inline]
     pub const fn try_from_secs_f32(secs: f32) -> Result<Duration, TryFromFloatSecsError> {
@@ -1478,7 +1478,7 @@ impl Duration {
     /// let res = Duration::try_from_secs_f64(val);
     /// assert_eq!(res, Ok(Duration::new(1, 2_929_688)));
     /// ```
-    #[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "duration_checked_float", since = "1.66.0")]
     #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")]
     #[inline]
     pub const fn try_from_secs_f64(secs: f64) -> Result<Duration, TryFromFloatSecsError> {
diff --git a/library/core/tests/fmt/float.rs b/library/core/tests/fmt/float.rs
index 47a7400f76e..003782f34dc 100644
--- a/library/core/tests/fmt/float.rs
+++ b/library/core/tests/fmt/float.rs
@@ -5,7 +5,7 @@ fn test_format_f64() {
     assert_eq!("10", format!("{:.0}", 9.9f64));
     assert_eq!("9.8", format!("{:.1}", 9.849f64));
     assert_eq!("9.9", format!("{:.1}", 9.851f64));
-    assert_eq!("1", format!("{:.0}", 0.5f64));
+    assert_eq!("0", format!("{:.0}", 0.5f64));
     assert_eq!("1.23456789e6", format!("{:e}", 1234567.89f64));
     assert_eq!("1.23456789e3", format!("{:e}", 1234.56789f64));
     assert_eq!("1.23456789E6", format!("{:E}", 1234567.89f64));
@@ -25,13 +25,73 @@ fn test_format_f64() {
 }
 
 #[test]
+fn test_format_f64_rounds_ties_to_even() {
+    assert_eq!("0", format!("{:.0}", 0.5f64));
+    assert_eq!("2", format!("{:.0}", 1.5f64));
+    assert_eq!("2", format!("{:.0}", 2.5f64));
+    assert_eq!("4", format!("{:.0}", 3.5f64));
+    assert_eq!("4", format!("{:.0}", 4.5f64));
+    assert_eq!("6", format!("{:.0}", 5.5f64));
+    assert_eq!("128", format!("{:.0}", 127.5f64));
+    assert_eq!("128", format!("{:.0}", 128.5f64));
+    assert_eq!("0.2", format!("{:.1}", 0.25f64));
+    assert_eq!("0.8", format!("{:.1}", 0.75f64));
+    assert_eq!("0.12", format!("{:.2}", 0.125f64));
+    assert_eq!("0.88", format!("{:.2}", 0.875f64));
+    assert_eq!("0.062", format!("{:.3}", 0.062f64));
+    assert_eq!("-0", format!("{:.0}", -0.5f64));
+    assert_eq!("-2", format!("{:.0}", -1.5f64));
+    assert_eq!("-2", format!("{:.0}", -2.5f64));
+    assert_eq!("-4", format!("{:.0}", -3.5f64));
+    assert_eq!("-4", format!("{:.0}", -4.5f64));
+    assert_eq!("-6", format!("{:.0}", -5.5f64));
+    assert_eq!("-128", format!("{:.0}", -127.5f64));
+    assert_eq!("-128", format!("{:.0}", -128.5f64));
+    assert_eq!("-0.2", format!("{:.1}", -0.25f64));
+    assert_eq!("-0.8", format!("{:.1}", -0.75f64));
+    assert_eq!("-0.12", format!("{:.2}", -0.125f64));
+    assert_eq!("-0.88", format!("{:.2}", -0.875f64));
+    assert_eq!("-0.062", format!("{:.3}", -0.062f64));
+
+    assert_eq!("2e0", format!("{:.0e}", 1.5f64));
+    assert_eq!("2e0", format!("{:.0e}", 2.5f64));
+    assert_eq!("4e0", format!("{:.0e}", 3.5f64));
+    assert_eq!("4e0", format!("{:.0e}", 4.5f64));
+    assert_eq!("6e0", format!("{:.0e}", 5.5f64));
+    assert_eq!("1.28e2", format!("{:.2e}", 127.5f64));
+    assert_eq!("1.28e2", format!("{:.2e}", 128.5f64));
+    assert_eq!("-2e0", format!("{:.0e}", -1.5f64));
+    assert_eq!("-2e0", format!("{:.0e}", -2.5f64));
+    assert_eq!("-4e0", format!("{:.0e}", -3.5f64));
+    assert_eq!("-4e0", format!("{:.0e}", -4.5f64));
+    assert_eq!("-6e0", format!("{:.0e}", -5.5f64));
+    assert_eq!("-1.28e2", format!("{:.2e}", -127.5f64));
+    assert_eq!("-1.28e2", format!("{:.2e}", -128.5f64));
+
+    assert_eq!("2E0", format!("{:.0E}", 1.5f64));
+    assert_eq!("2E0", format!("{:.0E}", 2.5f64));
+    assert_eq!("4E0", format!("{:.0E}", 3.5f64));
+    assert_eq!("4E0", format!("{:.0E}", 4.5f64));
+    assert_eq!("6E0", format!("{:.0E}", 5.5f64));
+    assert_eq!("1.28E2", format!("{:.2E}", 127.5f64));
+    assert_eq!("1.28E2", format!("{:.2E}", 128.5f64));
+    assert_eq!("-2E0", format!("{:.0E}", -1.5f64));
+    assert_eq!("-2E0", format!("{:.0E}", -2.5f64));
+    assert_eq!("-4E0", format!("{:.0E}", -3.5f64));
+    assert_eq!("-4E0", format!("{:.0E}", -4.5f64));
+    assert_eq!("-6E0", format!("{:.0E}", -5.5f64));
+    assert_eq!("-1.28E2", format!("{:.2E}", -127.5f64));
+    assert_eq!("-1.28E2", format!("{:.2E}", -128.5f64));
+}
+
+#[test]
 fn test_format_f32() {
     assert_eq!("1", format!("{:.0}", 1.0f32));
     assert_eq!("9", format!("{:.0}", 9.4f32));
     assert_eq!("10", format!("{:.0}", 9.9f32));
     assert_eq!("9.8", format!("{:.1}", 9.849f32));
     assert_eq!("9.9", format!("{:.1}", 9.851f32));
-    assert_eq!("1", format!("{:.0}", 0.5f32));
+    assert_eq!("0", format!("{:.0}", 0.5f32));
     assert_eq!("1.2345679e6", format!("{:e}", 1234567.89f32));
     assert_eq!("1.2345679e3", format!("{:e}", 1234.56789f32));
     assert_eq!("1.2345679E6", format!("{:E}", 1234567.89f32));
@@ -50,6 +110,66 @@ fn test_format_f32() {
     assert_eq!("1234.6", format!("{:.1?}", 1234.56789f32));
 }
 
+#[test]
+fn test_format_f32_rounds_ties_to_even() {
+    assert_eq!("0", format!("{:.0}", 0.5f32));
+    assert_eq!("2", format!("{:.0}", 1.5f32));
+    assert_eq!("2", format!("{:.0}", 2.5f32));
+    assert_eq!("4", format!("{:.0}", 3.5f32));
+    assert_eq!("4", format!("{:.0}", 4.5f32));
+    assert_eq!("6", format!("{:.0}", 5.5f32));
+    assert_eq!("128", format!("{:.0}", 127.5f32));
+    assert_eq!("128", format!("{:.0}", 128.5f32));
+    assert_eq!("0.2", format!("{:.1}", 0.25f32));
+    assert_eq!("0.8", format!("{:.1}", 0.75f32));
+    assert_eq!("0.12", format!("{:.2}", 0.125f32));
+    assert_eq!("0.88", format!("{:.2}", 0.875f32));
+    assert_eq!("0.062", format!("{:.3}", 0.062f32));
+    assert_eq!("-0", format!("{:.0}", -0.5f32));
+    assert_eq!("-2", format!("{:.0}", -1.5f32));
+    assert_eq!("-2", format!("{:.0}", -2.5f32));
+    assert_eq!("-4", format!("{:.0}", -3.5f32));
+    assert_eq!("-4", format!("{:.0}", -4.5f32));
+    assert_eq!("-6", format!("{:.0}", -5.5f32));
+    assert_eq!("-128", format!("{:.0}", -127.5f32));
+    assert_eq!("-128", format!("{:.0}", -128.5f32));
+    assert_eq!("-0.2", format!("{:.1}", -0.25f32));
+    assert_eq!("-0.8", format!("{:.1}", -0.75f32));
+    assert_eq!("-0.12", format!("{:.2}", -0.125f32));
+    assert_eq!("-0.88", format!("{:.2}", -0.875f32));
+    assert_eq!("-0.062", format!("{:.3}", -0.062f32));
+
+    assert_eq!("2e0", format!("{:.0e}", 1.5f32));
+    assert_eq!("2e0", format!("{:.0e}", 2.5f32));
+    assert_eq!("4e0", format!("{:.0e}", 3.5f32));
+    assert_eq!("4e0", format!("{:.0e}", 4.5f32));
+    assert_eq!("6e0", format!("{:.0e}", 5.5f32));
+    assert_eq!("1.28e2", format!("{:.2e}", 127.5f32));
+    assert_eq!("1.28e2", format!("{:.2e}", 128.5f32));
+    assert_eq!("-2e0", format!("{:.0e}", -1.5f32));
+    assert_eq!("-2e0", format!("{:.0e}", -2.5f32));
+    assert_eq!("-4e0", format!("{:.0e}", -3.5f32));
+    assert_eq!("-4e0", format!("{:.0e}", -4.5f32));
+    assert_eq!("-6e0", format!("{:.0e}", -5.5f32));
+    assert_eq!("-1.28e2", format!("{:.2e}", -127.5f32));
+    assert_eq!("-1.28e2", format!("{:.2e}", -128.5f32));
+
+    assert_eq!("2E0", format!("{:.0E}", 1.5f32));
+    assert_eq!("2E0", format!("{:.0E}", 2.5f32));
+    assert_eq!("4E0", format!("{:.0E}", 3.5f32));
+    assert_eq!("4E0", format!("{:.0E}", 4.5f32));
+    assert_eq!("6E0", format!("{:.0E}", 5.5f32));
+    assert_eq!("1.28E2", format!("{:.2E}", 127.5f32));
+    assert_eq!("1.28E2", format!("{:.2E}", 128.5f32));
+    assert_eq!("-2E0", format!("{:.0E}", -1.5f32));
+    assert_eq!("-2E0", format!("{:.0E}", -2.5f32));
+    assert_eq!("-4E0", format!("{:.0E}", -3.5f32));
+    assert_eq!("-4E0", format!("{:.0E}", -4.5f32));
+    assert_eq!("-6E0", format!("{:.0E}", -5.5f32));
+    assert_eq!("-1.28E2", format!("{:.2E}", -127.5f32));
+    assert_eq!("-1.28E2", format!("{:.2E}", -128.5f32));
+}
+
 fn is_exponential(s: &str) -> bool {
     s.contains("e") || s.contains("E")
 }
diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs
index 798473bbde3..30843cc3dd7 100644
--- a/library/core/tests/num/flt2dec/mod.rs
+++ b/library/core/tests/num/flt2dec/mod.rs
@@ -138,7 +138,7 @@ where
 
     // check exact rounding for zero- and negative-width cases
     let start;
-    if expected[0] >= b'5' {
+    if expected[0] > b'5' {
         try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1;
                    "zero-width rounding-up mismatch for v={v}: \
                     actual {actual:?}, expected {expected:?}",
@@ -1007,7 +1007,7 @@ where
     assert_eq!(to_string(f, 999.5, Minus, 3), "999.500");
     assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000");
 
-    assert_eq!(to_string(f, 0.5, Minus, 0), "1");
+    assert_eq!(to_string(f, 0.5, Minus, 0), "0");
     assert_eq!(to_string(f, 0.5, Minus, 1), "0.5");
     assert_eq!(to_string(f, 0.5, Minus, 2), "0.50");
     assert_eq!(to_string(f, 0.5, Minus, 3), "0.500");
diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs
index f36f7c26806..dca6321cf62 100644
--- a/library/core/tests/option.rs
+++ b/library/core/tests/option.rs
@@ -57,7 +57,7 @@ fn test_get_resource() {
 }
 
 #[test]
-#[cfg_attr(not(bootstrap), allow(for_loops_over_fallibles))]
+#[allow(for_loops_over_fallibles)]
 fn test_option_dance() {
     let x = Some(());
     let mut y = Some(5);
diff --git a/library/portable-simd/crates/core_simd/src/intrinsics.rs b/library/portable-simd/crates/core_simd/src/intrinsics.rs
index 962c83a78cb..704e6ed0159 100644
--- a/library/portable-simd/crates/core_simd/src/intrinsics.rs
+++ b/library/portable-simd/crates/core_simd/src/intrinsics.rs
@@ -103,7 +103,7 @@ extern "platform-intrinsic" {
     /// val: vector of values to select if a lane is masked
     /// ptr: vector of pointers to read from
     /// mask: a "wide" mask of integers, selects as if simd_select(mask, read(ptr), val)
-    /// note, the LLVM intrinsic accepts a mask vector of <N x i1>
+    /// note, the LLVM intrinsic accepts a mask vector of `<N x i1>`
     /// FIXME: review this if/when we fix up our mask story in general?
     pub(crate) fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T;
     /// llvm.masked.scatter
diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs
index 5a077a469d8..fc1e0bc426d 100644
--- a/library/portable-simd/crates/core_simd/src/ops.rs
+++ b/library/portable-simd/crates/core_simd/src/ops.rs
@@ -40,7 +40,7 @@ macro_rules! unsafe_base {
 
 /// SAFETY: This macro should not be used for anything except Shl or Shr, and passed the appropriate shift intrinsic.
 /// It handles performing a bitand in addition to calling the shift operator, so that the result
-/// is well-defined: LLVM can return a poison value if you shl, lshr, or ashr if rhs >= <Int>::BITS
+/// is well-defined: LLVM can return a poison value if you shl, lshr, or ashr if `rhs >= <Int>::BITS`
 /// At worst, this will maybe add another instruction and cycle,
 /// at best, it may open up more optimization opportunities,
 /// or simply be elided entirely, especially for SIMD ISAs which default to this.
diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index 8001fcff648..0d3fc2c5244 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -546,7 +546,7 @@ impl Span {
     /// Note: The observable result of a macro should only rely on the tokens and
     /// not on this source text. The result of this function is a best effort to
     /// be used for diagnostics only.
-    #[stable(feature = "proc_macro_source_text", since = "CURRENT_RUSTC_VERSION")]
+    #[stable(feature = "proc_macro_source_text", since = "1.66.0")]
     pub fn source_text(&self) -> Option<String> {
         self.0.source_text()
     }
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index daa6976c301..c10bfde4ddf 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -16,7 +16,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core" }
 libc = { version = "0.2.135", default-features = false, features = ['rustc-dep-of-std'] }
-compiler_builtins = { version = "0.1.73" }
+compiler_builtins = { version = "0.1.82" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
 hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] }
diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs
index 4f14fc28038..5453853e138 100644
--- a/library/std/src/net/ip_addr.rs
+++ b/library/std/src/net/ip_addr.rs
@@ -73,7 +73,6 @@ pub enum IpAddr {
 /// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex
 /// ```
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv4Addr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Ipv4Addr {
     octets: [u8; 4],
@@ -156,7 +155,6 @@ pub struct Ipv4Addr {
 /// assert_eq!(localhost.is_loopback(), true);
 /// ```
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-#[cfg_attr(not(test), rustc_diagnostic_item = "Ipv6Addr")]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Ipv6Addr {
     octets: [u8; 16],
diff --git a/library/std/src/os/android/net.rs b/library/std/src/os/android/net.rs
index ff96125c37b..7cecd1bbfaa 100644
--- a/library/std/src/os/android/net.rs
+++ b/library/std/src/os/android/net.rs
@@ -1,4 +1,9 @@
-//! Linux and Android-specific definitions for socket options.
+//! Android-specific networking functionality.
 
 #![unstable(feature = "tcp_quickack", issue = "96256")]
-pub use crate::os::net::tcp::TcpStreamExt;
+
+#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+pub use crate::os::net::linux_ext::addr::SocketAddrExt;
+
+#[unstable(feature = "tcp_quickack", issue = "96256")]
+pub use crate::os::net::linux_ext::tcp::TcpStreamExt;
diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs
index ff96125c37b..94081c8dd31 100644
--- a/library/std/src/os/linux/net.rs
+++ b/library/std/src/os/linux/net.rs
@@ -1,4 +1,9 @@
-//! Linux and Android-specific definitions for socket options.
+//! Linux-specific networking functionality.
 
 #![unstable(feature = "tcp_quickack", issue = "96256")]
-pub use crate::os::net::tcp::TcpStreamExt;
+
+#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+pub use crate::os::net::linux_ext::addr::SocketAddrExt;
+
+#[unstable(feature = "tcp_quickack", issue = "96256")]
+pub use crate::os::net::linux_ext::tcp::TcpStreamExt;
diff --git a/library/std/src/os/net/linux_ext/addr.rs b/library/std/src/os/net/linux_ext/addr.rs
new file mode 100644
index 00000000000..df3fc8e6a3b
--- /dev/null
+++ b/library/std/src/os/net/linux_ext/addr.rs
@@ -0,0 +1,64 @@
+//! Linux and Android-specific extensions to socket addresses.
+
+use crate::os::unix::net::SocketAddr;
+use crate::sealed::Sealed;
+
+/// Platform-specific extensions to [`SocketAddr`].
+#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+pub trait SocketAddrExt: Sealed {
+    /// Creates a Unix socket address in the abstract namespace.
+    ///
+    /// The abstract namespace is a Linux-specific extension that allows Unix
+    /// sockets to be bound without creating an entry in the filesystem.
+    /// Abstract sockets are unaffected by filesystem layout or permissions,
+    /// and no cleanup is necessary when the socket is closed.
+    ///
+    /// An abstract socket address name may contain any bytes, including zero.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the name is longer than `SUN_LEN - 1`.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_abstract)]
+    /// use std::os::unix::net::{UnixListener, SocketAddr};
+    /// use std::os::linux::net::SocketAddrExt;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let addr = SocketAddr::from_abstract_name(b"hidden")?;
+    ///     let listener = match UnixListener::bind_addr(&addr) {
+    ///         Ok(sock) => sock,
+    ///         Err(err) => {
+    ///             println!("Couldn't bind: {err:?}");
+    ///             return Err(err);
+    ///         }
+    ///     };
+    ///     Ok(())
+    /// }
+    /// ```
+    fn from_abstract_name<N>(name: &N) -> crate::io::Result<SocketAddr>
+    where
+        N: AsRef<[u8]>;
+
+    /// Returns the contents of this address if it is in the abstract namespace.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(unix_socket_abstract)]
+    /// use std::os::unix::net::{UnixListener, SocketAddr};
+    /// use std::os::linux::net::SocketAddrExt;
+    ///
+    /// fn main() -> std::io::Result<()> {
+    ///     let name = b"hidden";
+    ///     let name_addr = SocketAddr::from_abstract_name(name)?;
+    ///     let socket = UnixListener::bind_addr(&name_addr)?;
+    ///     let local_addr = socket.local_addr().expect("Couldn't get local address");
+    ///     assert_eq!(local_addr.as_abstract_name(), Some(&name[..]));
+    ///     Ok(())
+    /// }
+    /// ```
+    fn as_abstract_name(&self) -> Option<&[u8]>;
+}
diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs
new file mode 100644
index 00000000000..318ebacfd7a
--- /dev/null
+++ b/library/std/src/os/net/linux_ext/mod.rs
@@ -0,0 +1,12 @@
+//! Linux and Android-specific networking functionality.
+
+#![doc(cfg(any(target_os = "linux", target_os = "android")))]
+
+#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+pub(crate) mod addr;
+
+#[unstable(feature = "tcp_quickack", issue = "96256")]
+pub(crate) mod tcp;
+
+#[cfg(test)]
+mod tests;
diff --git a/library/std/src/os/net/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs
index 5e9ee65a415..5e9ee65a415 100644
--- a/library/std/src/os/net/tcp.rs
+++ b/library/std/src/os/net/linux_ext/tcp.rs
diff --git a/library/std/src/os/net/tests.rs b/library/std/src/os/net/linux_ext/tests.rs
index 4704e315691..2db4deed036 100644
--- a/library/std/src/os/net/tests.rs
+++ b/library/std/src/os/net/linux_ext/tests.rs
@@ -1,9 +1,8 @@
-#[cfg(any(target_os = "android", target_os = "linux",))]
 #[test]
 fn quickack() {
     use crate::{
         net::{test::next_test_ip4, TcpListener, TcpStream},
-        os::net::tcp::TcpStreamExt,
+        os::net::linux_ext::tcp::TcpStreamExt,
     };
 
     macro_rules! t {
diff --git a/library/std/src/os/net/mod.rs b/library/std/src/os/net/mod.rs
index d6d84d24ec4..5ec267c41e9 100644
--- a/library/std/src/os/net/mod.rs
+++ b/library/std/src/os/net/mod.rs
@@ -1,7 +1,4 @@
-//! Linux and Android-specific definitions for socket options.
+//! OS-specific networking functionality.
 
-#![unstable(feature = "tcp_quickack", issue = "96256")]
-#![doc(cfg(any(target_os = "linux", target_os = "android",)))]
-pub mod tcp;
-#[cfg(test)]
-mod tests;
+#[cfg(any(target_os = "linux", target_os = "android", doc))]
+pub(super) mod linux_ext;
diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs
index 094085e1942..81ac829d21b 100644
--- a/library/std/src/os/unix/net/addr.rs
+++ b/library/std/src/os/unix/net/addr.rs
@@ -1,6 +1,9 @@
 use crate::ffi::OsStr;
+#[cfg(any(doc, target_os = "android", target_os = "linux"))]
+use crate::os::net::linux_ext;
 use crate::os::unix::ffi::OsStrExt;
 use crate::path::Path;
+use crate::sealed::Sealed;
 use crate::sys::cvt;
 use crate::{fmt, io, mem, ptr};
 
@@ -224,31 +227,6 @@ impl SocketAddr {
         if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None }
     }
 
-    /// Returns the contents of this address if it is an abstract namespace
-    /// without the leading null byte.
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// #![feature(unix_socket_abstract)]
-    /// use std::os::unix::net::{UnixListener, SocketAddr};
-    ///
-    /// fn main() -> std::io::Result<()> {
-    ///     let namespace = b"hidden";
-    ///     let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?;
-    ///     let socket = UnixListener::bind_addr(&namespace_addr)?;
-    ///     let local_addr = socket.local_addr().expect("Couldn't get local address");
-    ///     assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..]));
-    ///     Ok(())
-    /// }
-    /// ```
-    #[doc(cfg(any(target_os = "android", target_os = "linux")))]
-    #[cfg(any(doc, target_os = "android", target_os = "linux",))]
-    #[unstable(feature = "unix_socket_abstract", issue = "85410")]
-    pub fn as_abstract_namespace(&self) -> Option<&[u8]> {
-        if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
-    }
-
     fn address(&self) -> AddressKind<'_> {
         let len = self.len as usize - sun_path_offset(&self.addr);
         let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
@@ -265,62 +243,41 @@ impl SocketAddr {
             AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
         }
     }
+}
 
-    /// Creates an abstract domain socket address from a namespace
-    ///
-    /// An abstract address does not create a file unlike traditional path-based
-    /// Unix sockets. The advantage of this is that the address will disappear when
-    /// the socket bound to it is closed, so no filesystem clean up is required.
-    ///
-    /// The leading null byte for the abstract namespace is automatically added.
-    ///
-    /// This is a Linux-specific extension. See more at [`unix(7)`].
-    ///
-    /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html
-    ///
-    /// # Errors
-    ///
-    /// This will return an error if the given namespace is too long
-    ///
-    /// # Examples
-    ///
-    /// ```no_run
-    /// #![feature(unix_socket_abstract)]
-    /// use std::os::unix::net::{UnixListener, SocketAddr};
-    ///
-    /// fn main() -> std::io::Result<()> {
-    ///     let addr = SocketAddr::from_abstract_namespace(b"hidden")?;
-    ///     let listener = match UnixListener::bind_addr(&addr) {
-    ///         Ok(sock) => sock,
-    ///         Err(err) => {
-    ///             println!("Couldn't bind: {err:?}");
-    ///             return Err(err);
-    ///         }
-    ///     };
-    ///     Ok(())
-    /// }
-    /// ```
-    #[doc(cfg(any(target_os = "android", target_os = "linux")))]
-    #[cfg(any(doc, target_os = "android", target_os = "linux",))]
-    #[unstable(feature = "unix_socket_abstract", issue = "85410")]
-    pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result<SocketAddr> {
+#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+impl Sealed for SocketAddr {}
+
+#[doc(cfg(any(target_os = "android", target_os = "linux")))]
+#[cfg(any(doc, target_os = "android", target_os = "linux"))]
+#[unstable(feature = "unix_socket_abstract", issue = "85410")]
+impl linux_ext::addr::SocketAddrExt for SocketAddr {
+    fn as_abstract_name(&self) -> Option<&[u8]> {
+        if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
+    }
+
+    fn from_abstract_name<N>(name: &N) -> crate::io::Result<Self>
+    where
+        N: AsRef<[u8]>,
+    {
+        let name = name.as_ref();
         unsafe {
             let mut addr: libc::sockaddr_un = mem::zeroed();
             addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
 
-            if namespace.len() + 1 > addr.sun_path.len() {
+            if name.len() + 1 > addr.sun_path.len() {
                 return Err(io::const_io_error!(
                     io::ErrorKind::InvalidInput,
-                    "namespace must be shorter than SUN_LEN",
+                    "abstract socket name must be shorter than SUN_LEN",
                 ));
             }
 
             crate::ptr::copy_nonoverlapping(
-                namespace.as_ptr(),
+                name.as_ptr(),
                 addr.sun_path.as_mut_ptr().add(1) as *mut u8,
-                namespace.len(),
+                name.len(),
             );
-            let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t;
+            let len = (sun_path_offset(&addr) + 1 + name.len()) as libc::socklen_t;
             SocketAddr::from_parts(addr, len)
         }
     }
diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs
index e4499f9b6a6..37fcfa8446b 100644
--- a/library/std/src/os/unix/net/tests.rs
+++ b/library/std/src/os/unix/net/tests.rs
@@ -7,6 +7,12 @@ use crate::sys_common::io::test::tmpdir;
 use crate::thread;
 use crate::time::Duration;
 
+#[cfg(target_os = "android")]
+use crate::os::android::net::SocketAddrExt;
+
+#[cfg(target_os = "linux")]
+use crate::os::linux::net::SocketAddrExt;
+
 macro_rules! or_panic {
     ($e:expr) => {
         match $e {
@@ -404,7 +410,7 @@ fn test_abstract_stream_connect() {
     let msg1 = b"hello";
     let msg2 = b"world";
 
-    let socket_addr = or_panic!(SocketAddr::from_abstract_namespace(b"namespace"));
+    let socket_addr = or_panic!(SocketAddr::from_abstract_name(b"name"));
     let listener = or_panic!(UnixListener::bind_addr(&socket_addr));
 
     let thread = thread::spawn(move || {
@@ -418,7 +424,7 @@ fn test_abstract_stream_connect() {
     let mut stream = or_panic!(UnixStream::connect_addr(&socket_addr));
 
     let peer = or_panic!(stream.peer_addr());
-    assert_eq!(peer.as_abstract_namespace().unwrap(), b"namespace");
+    assert_eq!(peer.as_abstract_name().unwrap(), b"name");
 
     or_panic!(stream.write_all(msg1));
     let mut buf = vec![];
@@ -432,7 +438,7 @@ fn test_abstract_stream_connect() {
 #[cfg(any(target_os = "android", target_os = "linux"))]
 #[test]
 fn test_abstract_stream_iter() {
-    let addr = or_panic!(SocketAddr::from_abstract_namespace(b"hidden"));
+    let addr = or_panic!(SocketAddr::from_abstract_name(b"hidden"));
     let listener = or_panic!(UnixListener::bind_addr(&addr));
 
     let thread = thread::spawn(move || {
@@ -454,13 +460,13 @@ fn test_abstract_stream_iter() {
 #[cfg(any(target_os = "android", target_os = "linux"))]
 #[test]
 fn test_abstract_datagram_bind_send_to_addr() {
-    let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns1"));
+    let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns1"));
     let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1));
 
     let local = or_panic!(sock1.local_addr());
-    assert_eq!(local.as_abstract_namespace().unwrap(), b"ns1");
+    assert_eq!(local.as_abstract_name().unwrap(), b"ns1");
 
-    let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns2"));
+    let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns2"));
     let sock2 = or_panic!(UnixDatagram::bind_addr(&addr2));
 
     let msg = b"hello world";
@@ -469,13 +475,13 @@ fn test_abstract_datagram_bind_send_to_addr() {
     let (len, addr) = or_panic!(sock2.recv_from(&mut buf));
     assert_eq!(msg, &buf[..]);
     assert_eq!(len, 11);
-    assert_eq!(addr.as_abstract_namespace().unwrap(), b"ns1");
+    assert_eq!(addr.as_abstract_name().unwrap(), b"ns1");
 }
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
 #[test]
 fn test_abstract_datagram_connect_addr() {
-    let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns3"));
+    let addr1 = or_panic!(SocketAddr::from_abstract_name(b"ns3"));
     let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1));
 
     let sock = or_panic!(UnixDatagram::unbound());
@@ -489,7 +495,7 @@ fn test_abstract_datagram_connect_addr() {
     assert_eq!(addr.is_unnamed(), true);
     assert_eq!(msg, &buf[..]);
 
-    let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns4"));
+    let addr2 = or_panic!(SocketAddr::from_abstract_name(b"ns4"));
     let bsock2 = or_panic!(UnixDatagram::bind_addr(&addr2));
 
     or_panic!(sock.connect_addr(&addr2));
@@ -499,8 +505,8 @@ fn test_abstract_datagram_connect_addr() {
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
 #[test]
-fn test_abstract_namespace_too_long() {
-    match SocketAddr::from_abstract_namespace(
+fn test_abstract_name_too_long() {
+    match SocketAddr::from_abstract_name(
         b"abcdefghijklmnopqrstuvwxyzabcdefghijklmn\
         opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi\
         jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",
@@ -513,11 +519,11 @@ fn test_abstract_namespace_too_long() {
 
 #[cfg(any(target_os = "android", target_os = "linux"))]
 #[test]
-fn test_abstract_namespace_no_pathname_and_not_unnamed() {
-    let namespace = b"local";
-    let addr = or_panic!(SocketAddr::from_abstract_namespace(&namespace[..]));
+fn test_abstract_no_pathname_and_not_unnamed() {
+    let name = b"local";
+    let addr = or_panic!(SocketAddr::from_abstract_name(name));
     assert_eq!(addr.as_pathname(), None);
-    assert_eq!(addr.as_abstract_namespace(), Some(&namespace[..]));
+    assert_eq!(addr.as_abstract_name(), Some(&name[..]));
     assert_eq!(addr.is_unnamed(), false);
 }
 
diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs
index 57bd842a50c..4e123a1eec8 100644
--- a/library/std/src/os/wasi/io/mod.rs
+++ b/library/std/src/os/wasi/io/mod.rs
@@ -1,6 +1,6 @@
 //! WASI-specific extensions to general I/O primitives.
 
-#![stable(feature = "io_safety", since = "1.63.0")]
+#![stable(feature = "io_safety_wasi", since = "1.65.0")]
 
-#[stable(feature = "io_safety", since = "1.63.0")]
+#[stable(feature = "io_safety_wasi", since = "1.65.0")]
 pub use crate::os::fd::*;
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 9d63281627d..af88b9070c1 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -2142,7 +2142,10 @@ impl Path {
 
     /// Returns the `Path` without its final component, if there is one.
     ///
-    /// Returns [`None`] if the path terminates in a root or prefix.
+    /// This means it returns `Some("")` for relative paths with one component.
+    ///
+    /// Returns [`None`] if the path terminates in a root or prefix, or if it's
+    /// the empty string.
     ///
     /// # Examples
     ///
@@ -2156,6 +2159,14 @@ impl Path {
     /// let grand_parent = parent.parent().unwrap();
     /// assert_eq!(grand_parent, Path::new("/"));
     /// assert_eq!(grand_parent.parent(), None);
+    ///
+    /// let relative_path = Path::new("foo/bar");
+    /// let parent = relative_path.parent();
+    /// assert_eq!(parent, Some(Path::new("foo")));
+    /// let grand_parent = parent.and_then(Path::parent);
+    /// assert_eq!(grand_parent, Some(Path::new("")));
+    /// let great_grand_parent = grand_parent.and_then(Path::parent);
+    /// assert_eq!(great_grand_parent, None);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     #[doc(alias = "dirname")]
diff --git a/library/std/src/personality/gcc.rs b/library/std/src/personality/gcc.rs
index 7f0b0439cf0..5fc1b91a1c3 100644
--- a/library/std/src/personality/gcc.rs
+++ b/library/std/src/personality/gcc.rs
@@ -219,7 +219,7 @@ cfg_if::cfg_if! {
         }
 
         cfg_if::cfg_if! {
-            if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] {
+            if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] {
                 // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind
                 // handler data (aka LSDA) uses GCC-compatible encoding.
                 #[lang = "eh_personality"]
diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs
index 7b507a169b3..4fee8d3e92f 100644
--- a/library/std/src/sync/mod.rs
+++ b/library/std/src/sync/mod.rs
@@ -182,6 +182,7 @@ pub mod mpsc;
 mod barrier;
 mod condvar;
 mod lazy_lock;
+mod mpmc;
 mod mutex;
 mod once;
 mod once_lock;
diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs
new file mode 100644
index 00000000000..4db7b4990b9
--- /dev/null
+++ b/library/std/src/sync/mpmc/array.rs
@@ -0,0 +1,513 @@
+//! Bounded channel based on a preallocated array.
+//!
+//! This flavor has a fixed, positive capacity.
+//!
+//! The implementation is based on Dmitry Vyukov's bounded MPMC queue.
+//!
+//! Source:
+//!   - <http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue>
+//!   - <https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub>
+
+use super::context::Context;
+use super::error::*;
+use super::select::{Operation, Selected, Token};
+use super::utils::{Backoff, CachePadded};
+use super::waker::SyncWaker;
+
+use crate::cell::UnsafeCell;
+use crate::mem::MaybeUninit;
+use crate::ptr;
+use crate::sync::atomic::{self, AtomicUsize, Ordering};
+use crate::time::Instant;
+
+/// A slot in a channel.
+struct Slot<T> {
+    /// The current stamp.
+    stamp: AtomicUsize,
+
+    /// The message in this slot.
+    msg: UnsafeCell<MaybeUninit<T>>,
+}
+
+/// The token type for the array flavor.
+#[derive(Debug)]
+pub(crate) struct ArrayToken {
+    /// Slot to read from or write to.
+    slot: *const u8,
+
+    /// Stamp to store into the slot after reading or writing.
+    stamp: usize,
+}
+
+impl Default for ArrayToken {
+    #[inline]
+    fn default() -> Self {
+        ArrayToken { slot: ptr::null(), stamp: 0 }
+    }
+}
+
+/// Bounded channel based on a preallocated array.
+pub(crate) struct Channel<T> {
+    /// The head of the channel.
+    ///
+    /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
+    /// packed into a single `usize`. The lower bits represent the index, while the upper bits
+    /// represent the lap. The mark bit in the head is always zero.
+    ///
+    /// Messages are popped from the head of the channel.
+    head: CachePadded<AtomicUsize>,
+
+    /// The tail of the channel.
+    ///
+    /// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
+    /// packed into a single `usize`. The lower bits represent the index, while the upper bits
+    /// represent the lap. The mark bit indicates that the channel is disconnected.
+    ///
+    /// Messages are pushed into the tail of the channel.
+    tail: CachePadded<AtomicUsize>,
+
+    /// The buffer holding slots.
+    buffer: Box<[Slot<T>]>,
+
+    /// The channel capacity.
+    cap: usize,
+
+    /// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`.
+    one_lap: usize,
+
+    /// If this bit is set in the tail, that means the channel is disconnected.
+    mark_bit: usize,
+
+    /// Senders waiting while the channel is full.
+    senders: SyncWaker,
+
+    /// Receivers waiting while the channel is empty and not disconnected.
+    receivers: SyncWaker,
+}
+
+impl<T> Channel<T> {
+    /// Creates a bounded channel of capacity `cap`.
+    pub(crate) fn with_capacity(cap: usize) -> Self {
+        assert!(cap > 0, "capacity must be positive");
+
+        // Compute constants `mark_bit` and `one_lap`.
+        let mark_bit = (cap + 1).next_power_of_two();
+        let one_lap = mark_bit * 2;
+
+        // Head is initialized to `{ lap: 0, mark: 0, index: 0 }`.
+        let head = 0;
+        // Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`.
+        let tail = 0;
+
+        // Allocate a buffer of `cap` slots initialized
+        // with stamps.
+        let buffer: Box<[Slot<T>]> = (0..cap)
+            .map(|i| {
+                // Set the stamp to `{ lap: 0, mark: 0, index: i }`.
+                Slot { stamp: AtomicUsize::new(i), msg: UnsafeCell::new(MaybeUninit::uninit()) }
+            })
+            .collect();
+
+        Channel {
+            buffer,
+            cap,
+            one_lap,
+            mark_bit,
+            head: CachePadded::new(AtomicUsize::new(head)),
+            tail: CachePadded::new(AtomicUsize::new(tail)),
+            senders: SyncWaker::new(),
+            receivers: SyncWaker::new(),
+        }
+    }
+
+    /// Attempts to reserve a slot for sending a message.
+    fn start_send(&self, token: &mut Token) -> bool {
+        let backoff = Backoff::new();
+        let mut tail = self.tail.load(Ordering::Relaxed);
+
+        loop {
+            // Check if the channel is disconnected.
+            if tail & self.mark_bit != 0 {
+                token.array.slot = ptr::null();
+                token.array.stamp = 0;
+                return true;
+            }
+
+            // Deconstruct the tail.
+            let index = tail & (self.mark_bit - 1);
+            let lap = tail & !(self.one_lap - 1);
+
+            // Inspect the corresponding slot.
+            debug_assert!(index < self.buffer.len());
+            let slot = unsafe { self.buffer.get_unchecked(index) };
+            let stamp = slot.stamp.load(Ordering::Acquire);
+
+            // If the tail and the stamp match, we may attempt to push.
+            if tail == stamp {
+                let new_tail = if index + 1 < self.cap {
+                    // Same lap, incremented index.
+                    // Set to `{ lap: lap, mark: 0, index: index + 1 }`.
+                    tail + 1
+                } else {
+                    // One lap forward, index wraps around to zero.
+                    // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
+                    lap.wrapping_add(self.one_lap)
+                };
+
+                // Try moving the tail.
+                match self.tail.compare_exchange_weak(
+                    tail,
+                    new_tail,
+                    Ordering::SeqCst,
+                    Ordering::Relaxed,
+                ) {
+                    Ok(_) => {
+                        // Prepare the token for the follow-up call to `write`.
+                        token.array.slot = slot as *const Slot<T> as *const u8;
+                        token.array.stamp = tail + 1;
+                        return true;
+                    }
+                    Err(_) => {
+                        backoff.spin();
+                        tail = self.tail.load(Ordering::Relaxed);
+                    }
+                }
+            } else if stamp.wrapping_add(self.one_lap) == tail + 1 {
+                atomic::fence(Ordering::SeqCst);
+                let head = self.head.load(Ordering::Relaxed);
+
+                // If the head lags one lap behind the tail as well...
+                if head.wrapping_add(self.one_lap) == tail {
+                    // ...then the channel is full.
+                    return false;
+                }
+
+                backoff.spin();
+                tail = self.tail.load(Ordering::Relaxed);
+            } else {
+                // Snooze because we need to wait for the stamp to get updated.
+                backoff.snooze();
+                tail = self.tail.load(Ordering::Relaxed);
+            }
+        }
+    }
+
+    /// Writes a message into the channel.
+    pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
+        // If there is no slot, the channel is disconnected.
+        if token.array.slot.is_null() {
+            return Err(msg);
+        }
+
+        let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
+
+        // Write the message into the slot and update the stamp.
+        slot.msg.get().write(MaybeUninit::new(msg));
+        slot.stamp.store(token.array.stamp, Ordering::Release);
+
+        // Wake a sleeping receiver.
+        self.receivers.notify();
+        Ok(())
+    }
+
+    /// Attempts to reserve a slot for receiving a message.
+    fn start_recv(&self, token: &mut Token) -> bool {
+        let backoff = Backoff::new();
+        let mut head = self.head.load(Ordering::Relaxed);
+
+        loop {
+            // Deconstruct the head.
+            let index = head & (self.mark_bit - 1);
+            let lap = head & !(self.one_lap - 1);
+
+            // Inspect the corresponding slot.
+            debug_assert!(index < self.buffer.len());
+            let slot = unsafe { self.buffer.get_unchecked(index) };
+            let stamp = slot.stamp.load(Ordering::Acquire);
+
+            // If the the stamp is ahead of the head by 1, we may attempt to pop.
+            if head + 1 == stamp {
+                let new = if index + 1 < self.cap {
+                    // Same lap, incremented index.
+                    // Set to `{ lap: lap, mark: 0, index: index + 1 }`.
+                    head + 1
+                } else {
+                    // One lap forward, index wraps around to zero.
+                    // Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
+                    lap.wrapping_add(self.one_lap)
+                };
+
+                // Try moving the head.
+                match self.head.compare_exchange_weak(
+                    head,
+                    new,
+                    Ordering::SeqCst,
+                    Ordering::Relaxed,
+                ) {
+                    Ok(_) => {
+                        // Prepare the token for the follow-up call to `read`.
+                        token.array.slot = slot as *const Slot<T> as *const u8;
+                        token.array.stamp = head.wrapping_add(self.one_lap);
+                        return true;
+                    }
+                    Err(_) => {
+                        backoff.spin();
+                        head = self.head.load(Ordering::Relaxed);
+                    }
+                }
+            } else if stamp == head {
+                atomic::fence(Ordering::SeqCst);
+                let tail = self.tail.load(Ordering::Relaxed);
+
+                // If the tail equals the head, that means the channel is empty.
+                if (tail & !self.mark_bit) == head {
+                    // If the channel is disconnected...
+                    if tail & self.mark_bit != 0 {
+                        // ...then receive an error.
+                        token.array.slot = ptr::null();
+                        token.array.stamp = 0;
+                        return true;
+                    } else {
+                        // Otherwise, the receive operation is not ready.
+                        return false;
+                    }
+                }
+
+                backoff.spin();
+                head = self.head.load(Ordering::Relaxed);
+            } else {
+                // Snooze because we need to wait for the stamp to get updated.
+                backoff.snooze();
+                head = self.head.load(Ordering::Relaxed);
+            }
+        }
+    }
+
+    /// Reads a message from the channel.
+    pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
+        if token.array.slot.is_null() {
+            // The channel is disconnected.
+            return Err(());
+        }
+
+        let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
+
+        // Read the message from the slot and update the stamp.
+        let msg = slot.msg.get().read().assume_init();
+        slot.stamp.store(token.array.stamp, Ordering::Release);
+
+        // Wake a sleeping sender.
+        self.senders.notify();
+        Ok(msg)
+    }
+
+    /// Attempts to send a message into the channel.
+    pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
+        let token = &mut Token::default();
+        if self.start_send(token) {
+            unsafe { self.write(token, msg).map_err(TrySendError::Disconnected) }
+        } else {
+            Err(TrySendError::Full(msg))
+        }
+    }
+
+    /// Sends a message into the channel.
+    pub(crate) fn send(
+        &self,
+        msg: T,
+        deadline: Option<Instant>,
+    ) -> Result<(), SendTimeoutError<T>> {
+        let token = &mut Token::default();
+        loop {
+            // Try sending a message several times.
+            let backoff = Backoff::new();
+            loop {
+                if self.start_send(token) {
+                    let res = unsafe { self.write(token, msg) };
+                    return res.map_err(SendTimeoutError::Disconnected);
+                }
+
+                if backoff.is_completed() {
+                    break;
+                } else {
+                    backoff.spin();
+                }
+            }
+
+            if let Some(d) = deadline {
+                if Instant::now() >= d {
+                    return Err(SendTimeoutError::Timeout(msg));
+                }
+            }
+
+            Context::with(|cx| {
+                // Prepare for blocking until a receiver wakes us up.
+                let oper = Operation::hook(token);
+                self.senders.register(oper, cx);
+
+                // Has the channel become ready just now?
+                if !self.is_full() || self.is_disconnected() {
+                    let _ = cx.try_select(Selected::Aborted);
+                }
+
+                // Block the current thread.
+                let sel = cx.wait_until(deadline);
+
+                match sel {
+                    Selected::Waiting => unreachable!(),
+                    Selected::Aborted | Selected::Disconnected => {
+                        self.senders.unregister(oper).unwrap();
+                    }
+                    Selected::Operation(_) => {}
+                }
+            });
+        }
+    }
+
+    /// Attempts to receive a message without blocking.
+    pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
+        let token = &mut Token::default();
+
+        if self.start_recv(token) {
+            unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
+        } else {
+            Err(TryRecvError::Empty)
+        }
+    }
+
+    /// Receives a message from the channel.
+    pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
+        let token = &mut Token::default();
+        loop {
+            if self.start_recv(token) {
+                let res = unsafe { self.read(token) };
+                return res.map_err(|_| RecvTimeoutError::Disconnected);
+            }
+
+            if let Some(d) = deadline {
+                if Instant::now() >= d {
+                    return Err(RecvTimeoutError::Timeout);
+                }
+            }
+
+            Context::with(|cx| {
+                // Prepare for blocking until a sender wakes us up.
+                let oper = Operation::hook(token);
+                self.receivers.register(oper, cx);
+
+                // Has the channel become ready just now?
+                if !self.is_empty() || self.is_disconnected() {
+                    let _ = cx.try_select(Selected::Aborted);
+                }
+
+                // Block the current thread.
+                let sel = cx.wait_until(deadline);
+
+                match sel {
+                    Selected::Waiting => unreachable!(),
+                    Selected::Aborted | Selected::Disconnected => {
+                        self.receivers.unregister(oper).unwrap();
+                        // If the channel was disconnected, we still have to check for remaining
+                        // messages.
+                    }
+                    Selected::Operation(_) => {}
+                }
+            });
+        }
+    }
+
+    /// Returns the current number of messages inside the channel.
+    pub(crate) fn len(&self) -> usize {
+        loop {
+            // Load the tail, then load the head.
+            let tail = self.tail.load(Ordering::SeqCst);
+            let head = self.head.load(Ordering::SeqCst);
+
+            // If the tail didn't change, we've got consistent values to work with.
+            if self.tail.load(Ordering::SeqCst) == tail {
+                let hix = head & (self.mark_bit - 1);
+                let tix = tail & (self.mark_bit - 1);
+
+                return if hix < tix {
+                    tix - hix
+                } else if hix > tix {
+                    self.cap - hix + tix
+                } else if (tail & !self.mark_bit) == head {
+                    0
+                } else {
+                    self.cap
+                };
+            }
+        }
+    }
+
+    /// Returns the capacity of the channel.
+    #[allow(clippy::unnecessary_wraps)] // This is intentional.
+    pub(crate) fn capacity(&self) -> Option<usize> {
+        Some(self.cap)
+    }
+
+    /// Disconnects the channel and wakes up all blocked senders and receivers.
+    ///
+    /// Returns `true` if this call disconnected the channel.
+    pub(crate) fn disconnect(&self) -> bool {
+        let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
+
+        if tail & self.mark_bit == 0 {
+            self.senders.disconnect();
+            self.receivers.disconnect();
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Returns `true` if the channel is disconnected.
+    pub(crate) fn is_disconnected(&self) -> bool {
+        self.tail.load(Ordering::SeqCst) & self.mark_bit != 0
+    }
+
+    /// Returns `true` if the channel is empty.
+    pub(crate) fn is_empty(&self) -> bool {
+        let head = self.head.load(Ordering::SeqCst);
+        let tail = self.tail.load(Ordering::SeqCst);
+
+        // Is the tail equal to the head?
+        //
+        // Note: If the head changes just before we load the tail, that means there was a moment
+        // when the channel was not empty, so it is safe to just return `false`.
+        (tail & !self.mark_bit) == head
+    }
+
+    /// Returns `true` if the channel is full.
+    pub(crate) fn is_full(&self) -> bool {
+        let tail = self.tail.load(Ordering::SeqCst);
+        let head = self.head.load(Ordering::SeqCst);
+
+        // Is the head lagging one lap behind tail?
+        //
+        // Note: If the tail changes just before we load the head, that means there was a moment
+        // when the channel was not full, so it is safe to just return `false`.
+        head.wrapping_add(self.one_lap) == tail & !self.mark_bit
+    }
+}
+
+impl<T> Drop for Channel<T> {
+    fn drop(&mut self) {
+        // Get the index of the head.
+        let hix = self.head.load(Ordering::Relaxed) & (self.mark_bit - 1);
+
+        // Loop over all slots that hold a message and drop them.
+        for i in 0..self.len() {
+            // Compute the index of the next slot holding a message.
+            let index = if hix + i < self.cap { hix + i } else { hix + i - self.cap };
+
+            unsafe {
+                debug_assert!(index < self.buffer.len());
+                let slot = self.buffer.get_unchecked_mut(index);
+                let msg = &mut *slot.msg.get();
+                msg.as_mut_ptr().drop_in_place();
+            }
+        }
+    }
+}
diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs
new file mode 100644
index 00000000000..bbfc6ce00ff
--- /dev/null
+++ b/library/std/src/sync/mpmc/context.rs
@@ -0,0 +1,155 @@
+//! Thread-local channel context.
+
+use super::select::Selected;
+use super::waker::current_thread_id;
+
+use crate::cell::Cell;
+use crate::ptr;
+use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
+use crate::sync::Arc;
+use crate::thread::{self, Thread};
+use crate::time::Instant;
+
+/// Thread-local context.
+#[derive(Debug, Clone)]
+pub struct Context {
+    inner: Arc<Inner>,
+}
+
+/// Inner representation of `Context`.
+#[derive(Debug)]
+struct Inner {
+    /// Selected operation.
+    select: AtomicUsize,
+
+    /// A slot into which another thread may store a pointer to its `Packet`.
+    packet: AtomicPtr<()>,
+
+    /// Thread handle.
+    thread: Thread,
+
+    /// Thread id.
+    thread_id: usize,
+}
+
+impl Context {
+    /// Creates a new context for the duration of the closure.
+    #[inline]
+    pub fn with<F, R>(f: F) -> R
+    where
+        F: FnOnce(&Context) -> R,
+    {
+        thread_local! {
+            /// Cached thread-local context.
+            static CONTEXT: Cell<Option<Context>> = Cell::new(Some(Context::new()));
+        }
+
+        let mut f = Some(f);
+        let mut f = |cx: &Context| -> R {
+            let f = f.take().unwrap();
+            f(cx)
+        };
+
+        CONTEXT
+            .try_with(|cell| match cell.take() {
+                None => f(&Context::new()),
+                Some(cx) => {
+                    cx.reset();
+                    let res = f(&cx);
+                    cell.set(Some(cx));
+                    res
+                }
+            })
+            .unwrap_or_else(|_| f(&Context::new()))
+    }
+
+    /// Creates a new `Context`.
+    #[cold]
+    fn new() -> Context {
+        Context {
+            inner: Arc::new(Inner {
+                select: AtomicUsize::new(Selected::Waiting.into()),
+                packet: AtomicPtr::new(ptr::null_mut()),
+                thread: thread::current(),
+                thread_id: current_thread_id(),
+            }),
+        }
+    }
+
+    /// Resets `select` and `packet`.
+    #[inline]
+    fn reset(&self) {
+        self.inner.select.store(Selected::Waiting.into(), Ordering::Release);
+        self.inner.packet.store(ptr::null_mut(), Ordering::Release);
+    }
+
+    /// Attempts to select an operation.
+    ///
+    /// On failure, the previously selected operation is returned.
+    #[inline]
+    pub fn try_select(&self, select: Selected) -> Result<(), Selected> {
+        self.inner
+            .select
+            .compare_exchange(
+                Selected::Waiting.into(),
+                select.into(),
+                Ordering::AcqRel,
+                Ordering::Acquire,
+            )
+            .map(|_| ())
+            .map_err(|e| e.into())
+    }
+
+    /// Stores a packet.
+    ///
+    /// This method must be called after `try_select` succeeds and there is a packet to provide.
+    #[inline]
+    pub fn store_packet(&self, packet: *mut ()) {
+        if !packet.is_null() {
+            self.inner.packet.store(packet, Ordering::Release);
+        }
+    }
+
+    /// Waits until an operation is selected and returns it.
+    ///
+    /// If the deadline is reached, `Selected::Aborted` will be selected.
+    #[inline]
+    pub fn wait_until(&self, deadline: Option<Instant>) -> Selected {
+        loop {
+            // Check whether an operation has been selected.
+            let sel = Selected::from(self.inner.select.load(Ordering::Acquire));
+            if sel != Selected::Waiting {
+                return sel;
+            }
+
+            // If there's a deadline, park the current thread until the deadline is reached.
+            if let Some(end) = deadline {
+                let now = Instant::now();
+
+                if now < end {
+                    thread::park_timeout(end - now);
+                } else {
+                    // The deadline has been reached. Try aborting select.
+                    return match self.try_select(Selected::Aborted) {
+                        Ok(()) => Selected::Aborted,
+                        Err(s) => s,
+                    };
+                }
+            } else {
+                thread::park();
+            }
+        }
+    }
+
+    /// Unparks the thread this context belongs to.
+    #[inline]
+    pub fn unpark(&self) {
+        self.inner.thread.unpark();
+    }
+
+    /// Returns the id of the thread this context belongs to.
+    #[inline]
+    pub fn thread_id(&self) -> usize {
+        self.inner.thread_id
+    }
+}
diff --git a/library/std/src/sync/mpmc/counter.rs b/library/std/src/sync/mpmc/counter.rs
new file mode 100644
index 00000000000..a5a6bdc67f1
--- /dev/null
+++ b/library/std/src/sync/mpmc/counter.rs
@@ -0,0 +1,137 @@
+use crate::ops;
+use crate::process;
+use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
+
+/// Reference counter internals.
+struct Counter<C> {
+    /// The number of senders associated with the channel.
+    senders: AtomicUsize,
+
+    /// The number of receivers associated with the channel.
+    receivers: AtomicUsize,
+
+    /// Set to `true` if the last sender or the last receiver reference deallocates the channel.
+    destroy: AtomicBool,
+
+    /// The internal channel.
+    chan: C,
+}
+
+/// Wraps a channel into the reference counter.
+pub(crate) fn new<C>(chan: C) -> (Sender<C>, Receiver<C>) {
+    let counter = Box::into_raw(Box::new(Counter {
+        senders: AtomicUsize::new(1),
+        receivers: AtomicUsize::new(1),
+        destroy: AtomicBool::new(false),
+        chan,
+    }));
+    let s = Sender { counter };
+    let r = Receiver { counter };
+    (s, r)
+}
+
+/// The sending side.
+pub(crate) struct Sender<C> {
+    counter: *mut Counter<C>,
+}
+
+impl<C> Sender<C> {
+    /// Returns the internal `Counter`.
+    fn counter(&self) -> &Counter<C> {
+        unsafe { &*self.counter }
+    }
+
+    /// Acquires another sender reference.
+    pub(crate) fn acquire(&self) -> Sender<C> {
+        let count = self.counter().senders.fetch_add(1, Ordering::Relaxed);
+
+        // Cloning senders and calling `mem::forget` on the clones could potentially overflow the
+        // counter. It's very difficult to recover sensibly from such degenerate scenarios so we
+        // just abort when the count becomes very large.
+        if count > isize::MAX as usize {
+            process::abort();
+        }
+
+        Sender { counter: self.counter }
+    }
+
+    /// Releases the sender reference.
+    ///
+    /// Function `disconnect` will be called if this is the last sender reference.
+    pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
+        if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 {
+            disconnect(&self.counter().chan);
+
+            if self.counter().destroy.swap(true, Ordering::AcqRel) {
+                drop(Box::from_raw(self.counter));
+            }
+        }
+    }
+}
+
+impl<C> ops::Deref for Sender<C> {
+    type Target = C;
+
+    fn deref(&self) -> &C {
+        &self.counter().chan
+    }
+}
+
+impl<C> PartialEq for Sender<C> {
+    fn eq(&self, other: &Sender<C>) -> bool {
+        self.counter == other.counter
+    }
+}
+
+/// The receiving side.
+pub(crate) struct Receiver<C> {
+    counter: *mut Counter<C>,
+}
+
+impl<C> Receiver<C> {
+    /// Returns the internal `Counter`.
+    fn counter(&self) -> &Counter<C> {
+        unsafe { &*self.counter }
+    }
+
+    /// Acquires another receiver reference.
+    pub(crate) fn acquire(&self) -> Receiver<C> {
+        let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed);
+
+        // Cloning receivers and calling `mem::forget` on the clones could potentially overflow the
+        // counter. It's very difficult to recover sensibly from such degenerate scenarios so we
+        // just abort when the count becomes very large.
+        if count > isize::MAX as usize {
+            process::abort();
+        }
+
+        Receiver { counter: self.counter }
+    }
+
+    /// Releases the receiver reference.
+    ///
+    /// Function `disconnect` will be called if this is the last receiver reference.
+    pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
+        if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 {
+            disconnect(&self.counter().chan);
+
+            if self.counter().destroy.swap(true, Ordering::AcqRel) {
+                drop(Box::from_raw(self.counter));
+            }
+        }
+    }
+}
+
+impl<C> ops::Deref for Receiver<C> {
+    type Target = C;
+
+    fn deref(&self) -> &C {
+        &self.counter().chan
+    }
+}
+
+impl<C> PartialEq for Receiver<C> {
+    fn eq(&self, other: &Receiver<C>) -> bool {
+        self.counter == other.counter
+    }
+}
diff --git a/library/std/src/sync/mpmc/error.rs b/library/std/src/sync/mpmc/error.rs
new file mode 100644
index 00000000000..1b8a1f38797
--- /dev/null
+++ b/library/std/src/sync/mpmc/error.rs
@@ -0,0 +1,46 @@
+use crate::error;
+use crate::fmt;
+
+pub use crate::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError};
+
+/// An error returned from the [`send_timeout`] method.
+///
+/// The error contains the message being sent so it can be recovered.
+///
+/// [`send_timeout`]: super::Sender::send_timeout
+#[derive(PartialEq, Eq, Clone, Copy)]
+pub enum SendTimeoutError<T> {
+    /// The message could not be sent because the channel is full and the operation timed out.
+    ///
+    /// If this is a zero-capacity channel, then the error indicates that there was no receiver
+    /// available to receive the message and the operation timed out.
+    Timeout(T),
+
+    /// The message could not be sent because the channel is disconnected.
+    Disconnected(T),
+}
+
+impl<T> fmt::Debug for SendTimeoutError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "SendTimeoutError(..)".fmt(f)
+    }
+}
+
+impl<T> fmt::Display for SendTimeoutError<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f),
+            SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f),
+        }
+    }
+}
+
+impl<T: Send> error::Error for SendTimeoutError<T> {}
+
+impl<T> From<SendError<T>> for SendTimeoutError<T> {
+    fn from(err: SendError<T>) -> SendTimeoutError<T> {
+        match err {
+            SendError(e) => SendTimeoutError::Disconnected(e),
+        }
+    }
+}
diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs
new file mode 100644
index 00000000000..2d5b2fb3b23
--- /dev/null
+++ b/library/std/src/sync/mpmc/list.rs
@@ -0,0 +1,638 @@
+//! Unbounded channel implemented as a linked list.
+
+use super::context::Context;
+use super::error::*;
+use super::select::{Operation, Selected, Token};
+use super::utils::{Backoff, CachePadded};
+use super::waker::SyncWaker;
+
+use crate::cell::UnsafeCell;
+use crate::marker::PhantomData;
+use crate::mem::MaybeUninit;
+use crate::ptr;
+use crate::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
+use crate::time::Instant;
+
+// Bits indicating the state of a slot:
+// * If a message has been written into the slot, `WRITE` is set.
+// * If a message has been read from the slot, `READ` is set.
+// * If the block is being destroyed, `DESTROY` is set.
+const WRITE: usize = 1;
+const READ: usize = 2;
+const DESTROY: usize = 4;
+
+// Each block covers one "lap" of indices.
+const LAP: usize = 32;
+// The maximum number of messages a block can hold.
+const BLOCK_CAP: usize = LAP - 1;
+// How many lower bits are reserved for metadata.
+const SHIFT: usize = 1;
+// Has two different purposes:
+// * If set in head, indicates that the block is not the last one.
+// * If set in tail, indicates that the channel is disconnected.
+const MARK_BIT: usize = 1;
+
+/// A slot in a block.
+struct Slot<T> {
+    /// The message.
+    msg: UnsafeCell<MaybeUninit<T>>,
+
+    /// The state of the slot.
+    state: AtomicUsize,
+}
+
+impl<T> Slot<T> {
+    /// Waits until a message is written into the slot.
+    fn wait_write(&self) {
+        let backoff = Backoff::new();
+        while self.state.load(Ordering::Acquire) & WRITE == 0 {
+            backoff.snooze();
+        }
+    }
+}
+
+/// A block in a linked list.
+///
+/// Each block in the list can hold up to `BLOCK_CAP` messages.
+struct Block<T> {
+    /// The next block in the linked list.
+    next: AtomicPtr<Block<T>>,
+
+    /// Slots for messages.
+    slots: [Slot<T>; BLOCK_CAP],
+}
+
+impl<T> Block<T> {
+    /// Creates an empty block.
+    fn new() -> Block<T> {
+        // SAFETY: This is safe because:
+        //  [1] `Block::next` (AtomicPtr) may be safely zero initialized.
+        //  [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4].
+        //  [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it
+        //       holds a MaybeUninit.
+        //  [4] `Slot::state` (AtomicUsize) may be safely zero initialized.
+        unsafe { MaybeUninit::zeroed().assume_init() }
+    }
+
+    /// Waits until the next pointer is set.
+    fn wait_next(&self) -> *mut Block<T> {
+        let backoff = Backoff::new();
+        loop {
+            let next = self.next.load(Ordering::Acquire);
+            if !next.is_null() {
+                return next;
+            }
+            backoff.snooze();
+        }
+    }
+
+    /// Sets the `DESTROY` bit in slots starting from `start` and destroys the block.
+    unsafe fn destroy(this: *mut Block<T>, start: usize) {
+        // It is not necessary to set the `DESTROY` bit in the last slot because that slot has
+        // begun destruction of the block.
+        for i in start..BLOCK_CAP - 1 {
+            let slot = (*this).slots.get_unchecked(i);
+
+            // Mark the `DESTROY` bit if a thread is still using the slot.
+            if slot.state.load(Ordering::Acquire) & READ == 0
+                && slot.state.fetch_or(DESTROY, Ordering::AcqRel) & READ == 0
+            {
+                // If a thread is still using the slot, it will continue destruction of the block.
+                return;
+            }
+        }
+
+        // No thread is using the block, now it is safe to destroy it.
+        drop(Box::from_raw(this));
+    }
+}
+
+/// A position in a channel.
+#[derive(Debug)]
+struct Position<T> {
+    /// The index in the channel.
+    index: AtomicUsize,
+
+    /// The block in the linked list.
+    block: AtomicPtr<Block<T>>,
+}
+
+/// The token type for the list flavor.
+#[derive(Debug)]
+pub(crate) struct ListToken {
+    /// The block of slots.
+    block: *const u8,
+
+    /// The offset into the block.
+    offset: usize,
+}
+
+impl Default for ListToken {
+    #[inline]
+    fn default() -> Self {
+        ListToken { block: ptr::null(), offset: 0 }
+    }
+}
+
+/// Unbounded channel implemented as a linked list.
+///
+/// Each message sent into the channel is assigned a sequence number, i.e. an index. Indices are
+/// represented as numbers of type `usize` and wrap on overflow.
+///
+/// Consecutive messages are grouped into blocks in order to put less pressure on the allocator and
+/// improve cache efficiency.
+pub(crate) struct Channel<T> {
+    /// The head of the channel.
+    head: CachePadded<Position<T>>,
+
+    /// The tail of the channel.
+    tail: CachePadded<Position<T>>,
+
+    /// Receivers waiting while the channel is empty and not disconnected.
+    receivers: SyncWaker,
+
+    /// Indicates that dropping a `Channel<T>` may drop messages of type `T`.
+    _marker: PhantomData<T>,
+}
+
+impl<T> Channel<T> {
+    /// Creates a new unbounded channel.
+    pub(crate) fn new() -> Self {
+        Channel {
+            head: CachePadded::new(Position {
+                block: AtomicPtr::new(ptr::null_mut()),
+                index: AtomicUsize::new(0),
+            }),
+            tail: CachePadded::new(Position {
+                block: AtomicPtr::new(ptr::null_mut()),
+                index: AtomicUsize::new(0),
+            }),
+            receivers: SyncWaker::new(),
+            _marker: PhantomData,
+        }
+    }
+
+    /// Attempts to reserve a slot for sending a message.
+    fn start_send(&self, token: &mut Token) -> bool {
+        let backoff = Backoff::new();
+        let mut tail = self.tail.index.load(Ordering::Acquire);
+        let mut block = self.tail.block.load(Ordering::Acquire);
+        let mut next_block = None;
+
+        loop {
+            // Check if the channel is disconnected.
+            if tail & MARK_BIT != 0 {
+                token.list.block = ptr::null();
+                return true;
+            }
+
+            // Calculate the offset of the index into the block.
+            let offset = (tail >> SHIFT) % LAP;
+
+            // If we reached the end of the block, wait until the next one is installed.
+            if offset == BLOCK_CAP {
+                backoff.snooze();
+                tail = self.tail.index.load(Ordering::Acquire);
+                block = self.tail.block.load(Ordering::Acquire);
+                continue;
+            }
+
+            // If we're going to have to install the next block, allocate it in advance in order to
+            // make the wait for other threads as short as possible.
+            if offset + 1 == BLOCK_CAP && next_block.is_none() {
+                next_block = Some(Box::new(Block::<T>::new()));
+            }
+
+            // If this is the first message to be sent into the channel, we need to allocate the
+            // first block and install it.
+            if block.is_null() {
+                let new = Box::into_raw(Box::new(Block::<T>::new()));
+
+                if self
+                    .tail
+                    .block
+                    .compare_exchange(block, new, Ordering::Release, Ordering::Relaxed)
+                    .is_ok()
+                {
+                    self.head.block.store(new, Ordering::Release);
+                    block = new;
+                } else {
+                    next_block = unsafe { Some(Box::from_raw(new)) };
+                    tail = self.tail.index.load(Ordering::Acquire);
+                    block = self.tail.block.load(Ordering::Acquire);
+                    continue;
+                }
+            }
+
+            let new_tail = tail + (1 << SHIFT);
+
+            // Try advancing the tail forward.
+            match self.tail.index.compare_exchange_weak(
+                tail,
+                new_tail,
+                Ordering::SeqCst,
+                Ordering::Acquire,
+            ) {
+                Ok(_) => unsafe {
+                    // If we've reached the end of the block, install the next one.
+                    if offset + 1 == BLOCK_CAP {
+                        let next_block = Box::into_raw(next_block.unwrap());
+                        self.tail.block.store(next_block, Ordering::Release);
+                        self.tail.index.fetch_add(1 << SHIFT, Ordering::Release);
+                        (*block).next.store(next_block, Ordering::Release);
+                    }
+
+                    token.list.block = block as *const u8;
+                    token.list.offset = offset;
+                    return true;
+                },
+                Err(_) => {
+                    backoff.spin();
+                    tail = self.tail.index.load(Ordering::Acquire);
+                    block = self.tail.block.load(Ordering::Acquire);
+                }
+            }
+        }
+    }
+
+    /// Writes a message into the channel.
+    pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
+        // If there is no slot, the channel is disconnected.
+        if token.list.block.is_null() {
+            return Err(msg);
+        }
+
+        // Write the message into the slot.
+        let block = token.list.block as *mut Block<T>;
+        let offset = token.list.offset;
+        let slot = (*block).slots.get_unchecked(offset);
+        slot.msg.get().write(MaybeUninit::new(msg));
+        slot.state.fetch_or(WRITE, Ordering::Release);
+
+        // Wake a sleeping receiver.
+        self.receivers.notify();
+        Ok(())
+    }
+
+    /// Attempts to reserve a slot for receiving a message.
+    fn start_recv(&self, token: &mut Token) -> bool {
+        let backoff = Backoff::new();
+        let mut head = self.head.index.load(Ordering::Acquire);
+        let mut block = self.head.block.load(Ordering::Acquire);
+
+        loop {
+            // Calculate the offset of the index into the block.
+            let offset = (head >> SHIFT) % LAP;
+
+            // If we reached the end of the block, wait until the next one is installed.
+            if offset == BLOCK_CAP {
+                backoff.snooze();
+                head = self.head.index.load(Ordering::Acquire);
+                block = self.head.block.load(Ordering::Acquire);
+                continue;
+            }
+
+            let mut new_head = head + (1 << SHIFT);
+
+            if new_head & MARK_BIT == 0 {
+                atomic::fence(Ordering::SeqCst);
+                let tail = self.tail.index.load(Ordering::Relaxed);
+
+                // If the tail equals the head, that means the channel is empty.
+                if head >> SHIFT == tail >> SHIFT {
+                    // If the channel is disconnected...
+                    if tail & MARK_BIT != 0 {
+                        // ...then receive an error.
+                        token.list.block = ptr::null();
+                        return true;
+                    } else {
+                        // Otherwise, the receive operation is not ready.
+                        return false;
+                    }
+                }
+
+                // If head and tail are not in the same block, set `MARK_BIT` in head.
+                if (head >> SHIFT) / LAP != (tail >> SHIFT) / LAP {
+                    new_head |= MARK_BIT;
+                }
+            }
+
+            // The block can be null here only if the first message is being sent into the channel.
+            // In that case, just wait until it gets initialized.
+            if block.is_null() {
+                backoff.snooze();
+                head = self.head.index.load(Ordering::Acquire);
+                block = self.head.block.load(Ordering::Acquire);
+                continue;
+            }
+
+            // Try moving the head index forward.
+            match self.head.index.compare_exchange_weak(
+                head,
+                new_head,
+                Ordering::SeqCst,
+                Ordering::Acquire,
+            ) {
+                Ok(_) => unsafe {
+                    // If we've reached the end of the block, move to the next one.
+                    if offset + 1 == BLOCK_CAP {
+                        let next = (*block).wait_next();
+                        let mut next_index = (new_head & !MARK_BIT).wrapping_add(1 << SHIFT);
+                        if !(*next).next.load(Ordering::Relaxed).is_null() {
+                            next_index |= MARK_BIT;
+                        }
+
+                        self.head.block.store(next, Ordering::Release);
+                        self.head.index.store(next_index, Ordering::Release);
+                    }
+
+                    token.list.block = block as *const u8;
+                    token.list.offset = offset;
+                    return true;
+                },
+                Err(_) => {
+                    backoff.spin();
+                    head = self.head.index.load(Ordering::Acquire);
+                    block = self.head.block.load(Ordering::Acquire);
+                }
+            }
+        }
+    }
+
+    /// Reads a message from the channel.
+    pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
+        if token.list.block.is_null() {
+            // The channel is disconnected.
+            return Err(());
+        }
+
+        // Read the message.
+        let block = token.list.block as *mut Block<T>;
+        let offset = token.list.offset;
+        let slot = (*block).slots.get_unchecked(offset);
+        slot.wait_write();
+        let msg = slot.msg.get().read().assume_init();
+
+        // Destroy the block if we've reached the end, or if another thread wanted to destroy but
+        // couldn't because we were busy reading from the slot.
+        if offset + 1 == BLOCK_CAP {
+            Block::destroy(block, 0);
+        } else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 {
+            Block::destroy(block, offset + 1);
+        }
+
+        Ok(msg)
+    }
+
+    /// Attempts to send a message into the channel.
+    pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
+        self.send(msg, None).map_err(|err| match err {
+            SendTimeoutError::Disconnected(msg) => TrySendError::Disconnected(msg),
+            SendTimeoutError::Timeout(_) => unreachable!(),
+        })
+    }
+
+    /// Sends a message into the channel.
+    pub(crate) fn send(
+        &self,
+        msg: T,
+        _deadline: Option<Instant>,
+    ) -> Result<(), SendTimeoutError<T>> {
+        let token = &mut Token::default();
+        assert!(self.start_send(token));
+        unsafe { self.write(token, msg).map_err(SendTimeoutError::Disconnected) }
+    }
+
+    /// Attempts to receive a message without blocking.
+    pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
+        let token = &mut Token::default();
+
+        if self.start_recv(token) {
+            unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
+        } else {
+            Err(TryRecvError::Empty)
+        }
+    }
+
+    /// Receives a message from the channel.
+    pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
+        let token = &mut Token::default();
+        loop {
+            if self.start_recv(token) {
+                unsafe {
+                    return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
+                }
+            }
+
+            if let Some(d) = deadline {
+                if Instant::now() >= d {
+                    return Err(RecvTimeoutError::Timeout);
+                }
+            }
+
+            // Prepare for blocking until a sender wakes us up.
+            Context::with(|cx| {
+                let oper = Operation::hook(token);
+                self.receivers.register(oper, cx);
+
+                // Has the channel become ready just now?
+                if !self.is_empty() || self.is_disconnected() {
+                    let _ = cx.try_select(Selected::Aborted);
+                }
+
+                // Block the current thread.
+                let sel = cx.wait_until(deadline);
+
+                match sel {
+                    Selected::Waiting => unreachable!(),
+                    Selected::Aborted | Selected::Disconnected => {
+                        self.receivers.unregister(oper).unwrap();
+                        // If the channel was disconnected, we still have to check for remaining
+                        // messages.
+                    }
+                    Selected::Operation(_) => {}
+                }
+            });
+        }
+    }
+
+    /// Returns the current number of messages inside the channel.
+    pub(crate) fn len(&self) -> usize {
+        loop {
+            // Load the tail index, then load the head index.
+            let mut tail = self.tail.index.load(Ordering::SeqCst);
+            let mut head = self.head.index.load(Ordering::SeqCst);
+
+            // If the tail index didn't change, we've got consistent indices to work with.
+            if self.tail.index.load(Ordering::SeqCst) == tail {
+                // Erase the lower bits.
+                tail &= !((1 << SHIFT) - 1);
+                head &= !((1 << SHIFT) - 1);
+
+                // Fix up indices if they fall onto block ends.
+                if (tail >> SHIFT) & (LAP - 1) == LAP - 1 {
+                    tail = tail.wrapping_add(1 << SHIFT);
+                }
+                if (head >> SHIFT) & (LAP - 1) == LAP - 1 {
+                    head = head.wrapping_add(1 << SHIFT);
+                }
+
+                // Rotate indices so that head falls into the first block.
+                let lap = (head >> SHIFT) / LAP;
+                tail = tail.wrapping_sub((lap * LAP) << SHIFT);
+                head = head.wrapping_sub((lap * LAP) << SHIFT);
+
+                // Remove the lower bits.
+                tail >>= SHIFT;
+                head >>= SHIFT;
+
+                // Return the difference minus the number of blocks between tail and head.
+                return tail - head - tail / LAP;
+            }
+        }
+    }
+
+    /// Returns the capacity of the channel.
+    pub(crate) fn capacity(&self) -> Option<usize> {
+        None
+    }
+
+    /// Disconnects senders and wakes up all blocked receivers.
+    ///
+    /// Returns `true` if this call disconnected the channel.
+    pub(crate) fn disconnect_senders(&self) -> bool {
+        let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
+
+        if tail & MARK_BIT == 0 {
+            self.receivers.disconnect();
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Disconnects receivers.
+    ///
+    /// Returns `true` if this call disconnected the channel.
+    pub(crate) fn disconnect_receivers(&self) -> bool {
+        let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
+
+        if tail & MARK_BIT == 0 {
+            // If receivers are dropped first, discard all messages to free
+            // memory eagerly.
+            self.discard_all_messages();
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Discards all messages.
+    ///
+    /// This method should only be called when all receivers are dropped.
+    fn discard_all_messages(&self) {
+        let backoff = Backoff::new();
+        let mut tail = self.tail.index.load(Ordering::Acquire);
+        loop {
+            let offset = (tail >> SHIFT) % LAP;
+            if offset != BLOCK_CAP {
+                break;
+            }
+
+            // New updates to tail will be rejected by MARK_BIT and aborted unless it's
+            // at boundary. We need to wait for the updates take affect otherwise there
+            // can be memory leaks.
+            backoff.snooze();
+            tail = self.tail.index.load(Ordering::Acquire);
+        }
+
+        let mut head = self.head.index.load(Ordering::Acquire);
+        let mut block = self.head.block.load(Ordering::Acquire);
+
+        unsafe {
+            // Drop all messages between head and tail and deallocate the heap-allocated blocks.
+            while head >> SHIFT != tail >> SHIFT {
+                let offset = (head >> SHIFT) % LAP;
+
+                if offset < BLOCK_CAP {
+                    // Drop the message in the slot.
+                    let slot = (*block).slots.get_unchecked(offset);
+                    slot.wait_write();
+                    let p = &mut *slot.msg.get();
+                    p.as_mut_ptr().drop_in_place();
+                } else {
+                    (*block).wait_next();
+                    // Deallocate the block and move to the next one.
+                    let next = (*block).next.load(Ordering::Acquire);
+                    drop(Box::from_raw(block));
+                    block = next;
+                }
+
+                head = head.wrapping_add(1 << SHIFT);
+            }
+
+            // Deallocate the last remaining block.
+            if !block.is_null() {
+                drop(Box::from_raw(block));
+            }
+        }
+        head &= !MARK_BIT;
+        self.head.block.store(ptr::null_mut(), Ordering::Release);
+        self.head.index.store(head, Ordering::Release);
+    }
+
+    /// Returns `true` if the channel is disconnected.
+    pub(crate) fn is_disconnected(&self) -> bool {
+        self.tail.index.load(Ordering::SeqCst) & MARK_BIT != 0
+    }
+
+    /// Returns `true` if the channel is empty.
+    pub(crate) fn is_empty(&self) -> bool {
+        let head = self.head.index.load(Ordering::SeqCst);
+        let tail = self.tail.index.load(Ordering::SeqCst);
+        head >> SHIFT == tail >> SHIFT
+    }
+
+    /// Returns `true` if the channel is full.
+    pub(crate) fn is_full(&self) -> bool {
+        false
+    }
+}
+
+impl<T> Drop for Channel<T> {
+    fn drop(&mut self) {
+        let mut head = self.head.index.load(Ordering::Relaxed);
+        let mut tail = self.tail.index.load(Ordering::Relaxed);
+        let mut block = self.head.block.load(Ordering::Relaxed);
+
+        // Erase the lower bits.
+        head &= !((1 << SHIFT) - 1);
+        tail &= !((1 << SHIFT) - 1);
+
+        unsafe {
+            // Drop all messages between head and tail and deallocate the heap-allocated blocks.
+            while head != tail {
+                let offset = (head >> SHIFT) % LAP;
+
+                if offset < BLOCK_CAP {
+                    // Drop the message in the slot.
+                    let slot = (*block).slots.get_unchecked(offset);
+                    let p = &mut *slot.msg.get();
+                    p.as_mut_ptr().drop_in_place();
+                } else {
+                    // Deallocate the block and move to the next one.
+                    let next = (*block).next.load(Ordering::Relaxed);
+                    drop(Box::from_raw(block));
+                    block = next;
+                }
+
+                head = head.wrapping_add(1 << SHIFT);
+            }
+
+            // Deallocate the last remaining block.
+            if !block.is_null() {
+                drop(Box::from_raw(block));
+            }
+        }
+    }
+}
diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs
new file mode 100644
index 00000000000..cef99c58843
--- /dev/null
+++ b/library/std/src/sync/mpmc/mod.rs
@@ -0,0 +1,430 @@
+//! Multi-producer multi-consumer channels.
+
+// This module is not currently exposed publicly, but is used
+// as the implementation for the channels in `sync::mpsc`. The
+// implementation comes from the crossbeam-channel crate:
+//
+// Copyright (c) 2019 The Crossbeam Project Developers
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and associated
+// documentation files (the "Software"), to deal in 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:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// 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 AUTHORS 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 IN THE SOFTWARE.
+
+mod array;
+mod context;
+mod counter;
+mod error;
+mod list;
+mod select;
+mod utils;
+mod waker;
+mod zero;
+
+use crate::fmt;
+use crate::panic::{RefUnwindSafe, UnwindSafe};
+use crate::time::{Duration, Instant};
+use error::*;
+
+/// Creates a channel of unbounded capacity.
+///
+/// This channel has a growable buffer that can hold any number of messages at a time.
+pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
+    let (s, r) = counter::new(list::Channel::new());
+    let s = Sender { flavor: SenderFlavor::List(s) };
+    let r = Receiver { flavor: ReceiverFlavor::List(r) };
+    (s, r)
+}
+
+/// Creates a channel of bounded capacity.
+///
+/// This channel has a buffer that can hold at most `cap` messages at a time.
+///
+/// A special case is zero-capacity channel, which cannot hold any messages. Instead, send and
+/// receive operations must appear at the same time in order to pair up and pass the message over.
+pub fn sync_channel<T>(cap: usize) -> (Sender<T>, Receiver<T>) {
+    if cap == 0 {
+        let (s, r) = counter::new(zero::Channel::new());
+        let s = Sender { flavor: SenderFlavor::Zero(s) };
+        let r = Receiver { flavor: ReceiverFlavor::Zero(r) };
+        (s, r)
+    } else {
+        let (s, r) = counter::new(array::Channel::with_capacity(cap));
+        let s = Sender { flavor: SenderFlavor::Array(s) };
+        let r = Receiver { flavor: ReceiverFlavor::Array(r) };
+        (s, r)
+    }
+}
+
+/// The sending side of a channel.
+pub struct Sender<T> {
+    flavor: SenderFlavor<T>,
+}
+
+/// Sender flavors.
+enum SenderFlavor<T> {
+    /// Bounded channel based on a preallocated array.
+    Array(counter::Sender<array::Channel<T>>),
+
+    /// Unbounded channel implemented as a linked list.
+    List(counter::Sender<list::Channel<T>>),
+
+    /// Zero-capacity channel.
+    Zero(counter::Sender<zero::Channel<T>>),
+}
+
+unsafe impl<T: Send> Send for Sender<T> {}
+unsafe impl<T: Send> Sync for Sender<T> {}
+
+impl<T> UnwindSafe for Sender<T> {}
+impl<T> RefUnwindSafe for Sender<T> {}
+
+impl<T> Sender<T> {
+    /// Attempts to send a message into the channel without blocking.
+    ///
+    /// This method will either send a message into the channel immediately or return an error if
+    /// the channel is full or disconnected. The returned error contains the original message.
+    ///
+    /// If called on a zero-capacity channel, this method will send the message only if there
+    /// happens to be a receive operation on the other side of the channel at the same time.
+    pub fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
+        match &self.flavor {
+            SenderFlavor::Array(chan) => chan.try_send(msg),
+            SenderFlavor::List(chan) => chan.try_send(msg),
+            SenderFlavor::Zero(chan) => chan.try_send(msg),
+        }
+    }
+
+    /// Blocks the current thread until a message is sent or the channel is disconnected.
+    ///
+    /// If the channel is full and not disconnected, this call will block until the send operation
+    /// can proceed. If the channel becomes disconnected, this call will wake up and return an
+    /// error. The returned error contains the original message.
+    ///
+    /// If called on a zero-capacity channel, this method will wait for a receive operation to
+    /// appear on the other side of the channel.
+    pub fn send(&self, msg: T) -> Result<(), SendError<T>> {
+        match &self.flavor {
+            SenderFlavor::Array(chan) => chan.send(msg, None),
+            SenderFlavor::List(chan) => chan.send(msg, None),
+            SenderFlavor::Zero(chan) => chan.send(msg, None),
+        }
+        .map_err(|err| match err {
+            SendTimeoutError::Disconnected(msg) => SendError(msg),
+            SendTimeoutError::Timeout(_) => unreachable!(),
+        })
+    }
+}
+
+// The methods below are not used by `sync::mpsc`, but
+// are useful and we'll likely want to expose them
+// eventually
+#[allow(unused)]
+impl<T> Sender<T> {
+    /// Waits for a message to be sent into the channel, but only for a limited time.
+    ///
+    /// If the channel is full and not disconnected, this call will block until the send operation
+    /// can proceed or the operation times out. If the channel becomes disconnected, this call will
+    /// wake up and return an error. The returned error contains the original message.
+    ///
+    /// If called on a zero-capacity channel, this method will wait for a receive operation to
+    /// appear on the other side of the channel.
+    pub fn send_timeout(&self, msg: T, timeout: Duration) -> Result<(), SendTimeoutError<T>> {
+        match Instant::now().checked_add(timeout) {
+            Some(deadline) => self.send_deadline(msg, deadline),
+            // So far in the future that it's practically the same as waiting indefinitely.
+            None => self.send(msg).map_err(SendTimeoutError::from),
+        }
+    }
+
+    /// Waits for a message to be sent into the channel, but only until a given deadline.
+    ///
+    /// If the channel is full and not disconnected, this call will block until the send operation
+    /// can proceed or the operation times out. If the channel becomes disconnected, this call will
+    /// wake up and return an error. The returned error contains the original message.
+    ///
+    /// If called on a zero-capacity channel, this method will wait for a receive operation to
+    /// appear on the other side of the channel.
+    pub fn send_deadline(&self, msg: T, deadline: Instant) -> Result<(), SendTimeoutError<T>> {
+        match &self.flavor {
+            SenderFlavor::Array(chan) => chan.send(msg, Some(deadline)),
+            SenderFlavor::List(chan) => chan.send(msg, Some(deadline)),
+            SenderFlavor::Zero(chan) => chan.send(msg, Some(deadline)),
+        }
+    }
+
+    /// Returns `true` if the channel is empty.
+    ///
+    /// Note: Zero-capacity channels are always empty.
+    pub fn is_empty(&self) -> bool {
+        match &self.flavor {
+            SenderFlavor::Array(chan) => chan.is_empty(),
+            SenderFlavor::List(chan) => chan.is_empty(),
+            SenderFlavor::Zero(chan) => chan.is_empty(),
+        }
+    }
+
+    /// Returns `true` if the channel is full.
+    ///
+    /// Note: Zero-capacity channels are always full.
+    pub fn is_full(&self) -> bool {
+        match &self.flavor {
+            SenderFlavor::Array(chan) => chan.is_full(),
+            SenderFlavor::List(chan) => chan.is_full(),
+            SenderFlavor::Zero(chan) => chan.is_full(),
+        }
+    }
+
+    /// Returns the number of messages in the channel.
+    pub fn len(&self) -> usize {
+        match &self.flavor {
+            SenderFlavor::Array(chan) => chan.len(),
+            SenderFlavor::List(chan) => chan.len(),
+            SenderFlavor::Zero(chan) => chan.len(),
+        }
+    }
+
+    /// If the channel is bounded, returns its capacity.
+    pub fn capacity(&self) -> Option<usize> {
+        match &self.flavor {
+            SenderFlavor::Array(chan) => chan.capacity(),
+            SenderFlavor::List(chan) => chan.capacity(),
+            SenderFlavor::Zero(chan) => chan.capacity(),
+        }
+    }
+
+    /// Returns `true` if senders belong to the same channel.
+    pub fn same_channel(&self, other: &Sender<T>) -> bool {
+        match (&self.flavor, &other.flavor) {
+            (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b,
+            (SenderFlavor::List(ref a), SenderFlavor::List(ref b)) => a == b,
+            (SenderFlavor::Zero(ref a), SenderFlavor::Zero(ref b)) => a == b,
+            _ => false,
+        }
+    }
+}
+
+impl<T> Drop for Sender<T> {
+    fn drop(&mut self) {
+        unsafe {
+            match &self.flavor {
+                SenderFlavor::Array(chan) => chan.release(|c| c.disconnect()),
+                SenderFlavor::List(chan) => chan.release(|c| c.disconnect_senders()),
+                SenderFlavor::Zero(chan) => chan.release(|c| c.disconnect()),
+            }
+        }
+    }
+}
+
+impl<T> Clone for Sender<T> {
+    fn clone(&self) -> Self {
+        let flavor = match &self.flavor {
+            SenderFlavor::Array(chan) => SenderFlavor::Array(chan.acquire()),
+            SenderFlavor::List(chan) => SenderFlavor::List(chan.acquire()),
+            SenderFlavor::Zero(chan) => SenderFlavor::Zero(chan.acquire()),
+        };
+
+        Sender { flavor }
+    }
+}
+
+impl<T> fmt::Debug for Sender<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.pad("Sender { .. }")
+    }
+}
+
+/// The receiving side of a channel.
+pub struct Receiver<T> {
+    flavor: ReceiverFlavor<T>,
+}
+
+/// Receiver flavors.
+enum ReceiverFlavor<T> {
+    /// Bounded channel based on a preallocated array.
+    Array(counter::Receiver<array::Channel<T>>),
+
+    /// Unbounded channel implemented as a linked list.
+    List(counter::Receiver<list::Channel<T>>),
+
+    /// Zero-capacity channel.
+    Zero(counter::Receiver<zero::Channel<T>>),
+}
+
+unsafe impl<T: Send> Send for Receiver<T> {}
+unsafe impl<T: Send> Sync for Receiver<T> {}
+
+impl<T> UnwindSafe for Receiver<T> {}
+impl<T> RefUnwindSafe for Receiver<T> {}
+
+impl<T> Receiver<T> {
+    /// Attempts to receive a message from the channel without blocking.
+    ///
+    /// This method will either receive a message from the channel immediately or return an error
+    /// if the channel is empty.
+    ///
+    /// If called on a zero-capacity channel, this method will receive a message only if there
+    /// happens to be a send operation on the other side of the channel at the same time.
+    pub fn try_recv(&self) -> Result<T, TryRecvError> {
+        match &self.flavor {
+            ReceiverFlavor::Array(chan) => chan.try_recv(),
+            ReceiverFlavor::List(chan) => chan.try_recv(),
+            ReceiverFlavor::Zero(chan) => chan.try_recv(),
+        }
+    }
+
+    /// Blocks the current thread until a message is received or the channel is empty and
+    /// disconnected.
+    ///
+    /// If the channel is empty and not disconnected, this call will block until the receive
+    /// operation can proceed. If the channel is empty and becomes disconnected, this call will
+    /// wake up and return an error.
+    ///
+    /// If called on a zero-capacity channel, this method will wait for a send operation to appear
+    /// on the other side of the channel.
+    pub fn recv(&self) -> Result<T, RecvError> {
+        match &self.flavor {
+            ReceiverFlavor::Array(chan) => chan.recv(None),
+            ReceiverFlavor::List(chan) => chan.recv(None),
+            ReceiverFlavor::Zero(chan) => chan.recv(None),
+        }
+        .map_err(|_| RecvError)
+    }
+
+    /// Waits for a message to be received from the channel, but only for a limited time.
+    ///
+    /// If the channel is empty and not disconnected, this call will block until the receive
+    /// operation can proceed or the operation times out. If the channel is empty and becomes
+    /// disconnected, this call will wake up and return an error.
+    ///
+    /// If called on a zero-capacity channel, this method will wait for a send operation to appear
+    /// on the other side of the channel.
+    pub fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
+        match Instant::now().checked_add(timeout) {
+            Some(deadline) => self.recv_deadline(deadline),
+            // So far in the future that it's practically the same as waiting indefinitely.
+            None => self.recv().map_err(RecvTimeoutError::from),
+        }
+    }
+
+    /// Waits for a message to be received from the channel, but only for a limited time.
+    ///
+    /// If the channel is empty and not disconnected, this call will block until the receive
+    /// operation can proceed or the operation times out. If the channel is empty and becomes
+    /// disconnected, this call will wake up and return an error.
+    ///
+    /// If called on a zero-capacity channel, this method will wait for a send operation to appear
+    /// on the other side of the channel.
+    pub fn recv_deadline(&self, deadline: Instant) -> Result<T, RecvTimeoutError> {
+        match &self.flavor {
+            ReceiverFlavor::Array(chan) => chan.recv(Some(deadline)),
+            ReceiverFlavor::List(chan) => chan.recv(Some(deadline)),
+            ReceiverFlavor::Zero(chan) => chan.recv(Some(deadline)),
+        }
+    }
+}
+
+// The methods below are not used by `sync::mpsc`, but
+// are useful and we'll likely want to expose them
+// eventually
+#[allow(unused)]
+impl<T> Receiver<T> {
+    /// Returns `true` if the channel is empty.
+    ///
+    /// Note: Zero-capacity channels are always empty.
+    pub fn is_empty(&self) -> bool {
+        match &self.flavor {
+            ReceiverFlavor::Array(chan) => chan.is_empty(),
+            ReceiverFlavor::List(chan) => chan.is_empty(),
+            ReceiverFlavor::Zero(chan) => chan.is_empty(),
+        }
+    }
+
+    /// Returns `true` if the channel is full.
+    ///
+    /// Note: Zero-capacity channels are always full.
+    pub fn is_full(&self) -> bool {
+        match &self.flavor {
+            ReceiverFlavor::Array(chan) => chan.is_full(),
+            ReceiverFlavor::List(chan) => chan.is_full(),
+            ReceiverFlavor::Zero(chan) => chan.is_full(),
+        }
+    }
+
+    /// Returns the number of messages in the channel.
+    pub fn len(&self) -> usize {
+        match &self.flavor {
+            ReceiverFlavor::Array(chan) => chan.len(),
+            ReceiverFlavor::List(chan) => chan.len(),
+            ReceiverFlavor::Zero(chan) => chan.len(),
+        }
+    }
+
+    /// If the channel is bounded, returns its capacity.
+    pub fn capacity(&self) -> Option<usize> {
+        match &self.flavor {
+            ReceiverFlavor::Array(chan) => chan.capacity(),
+            ReceiverFlavor::List(chan) => chan.capacity(),
+            ReceiverFlavor::Zero(chan) => chan.capacity(),
+        }
+    }
+
+    /// Returns `true` if receivers belong to the same channel.
+    pub fn same_channel(&self, other: &Receiver<T>) -> bool {
+        match (&self.flavor, &other.flavor) {
+            (ReceiverFlavor::Array(a), ReceiverFlavor::Array(b)) => a == b,
+            (ReceiverFlavor::List(a), ReceiverFlavor::List(b)) => a == b,
+            (ReceiverFlavor::Zero(a), ReceiverFlavor::Zero(b)) => a == b,
+            _ => false,
+        }
+    }
+}
+
+impl<T> Drop for Receiver<T> {
+    fn drop(&mut self) {
+        unsafe {
+            match &self.flavor {
+                ReceiverFlavor::Array(chan) => chan.release(|c| c.disconnect()),
+                ReceiverFlavor::List(chan) => chan.release(|c| c.disconnect_receivers()),
+                ReceiverFlavor::Zero(chan) => chan.release(|c| c.disconnect()),
+            }
+        }
+    }
+}
+
+impl<T> Clone for Receiver<T> {
+    fn clone(&self) -> Self {
+        let flavor = match &self.flavor {
+            ReceiverFlavor::Array(chan) => ReceiverFlavor::Array(chan.acquire()),
+            ReceiverFlavor::List(chan) => ReceiverFlavor::List(chan.acquire()),
+            ReceiverFlavor::Zero(chan) => ReceiverFlavor::Zero(chan.acquire()),
+        };
+
+        Receiver { flavor }
+    }
+}
+
+impl<T> fmt::Debug for Receiver<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.pad("Receiver { .. }")
+    }
+}
diff --git a/library/std/src/sync/mpmc/select.rs b/library/std/src/sync/mpmc/select.rs
new file mode 100644
index 00000000000..56a83fee2e1
--- /dev/null
+++ b/library/std/src/sync/mpmc/select.rs
@@ -0,0 +1,71 @@
+/// Temporary data that gets initialized during a blocking operation, and is consumed by
+/// `read` or `write`.
+///
+/// Each field contains data associated with a specific channel flavor.
+#[derive(Debug, Default)]
+pub struct Token {
+    pub(crate) array: super::array::ArrayToken,
+    pub(crate) list: super::list::ListToken,
+    #[allow(dead_code)]
+    pub(crate) zero: super::zero::ZeroToken,
+}
+
+/// Identifier associated with an operation by a specific thread on a specific channel.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Operation(usize);
+
+impl Operation {
+    /// Creates an operation identifier from a mutable reference.
+    ///
+    /// This function essentially just turns the address of the reference into a number. The
+    /// reference should point to a variable that is specific to the thread and the operation,
+    /// and is alive for the entire duration of a blocking operation.
+    #[inline]
+    pub fn hook<T>(r: &mut T) -> Operation {
+        let val = r as *mut T as usize;
+        // Make sure that the pointer address doesn't equal the numerical representation of
+        // `Selected::{Waiting, Aborted, Disconnected}`.
+        assert!(val > 2);
+        Operation(val)
+    }
+}
+
+/// Current state of a blocking operation.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Selected {
+    /// Still waiting for an operation.
+    Waiting,
+
+    /// The attempt to block the current thread has been aborted.
+    Aborted,
+
+    /// An operation became ready because a channel is disconnected.
+    Disconnected,
+
+    /// An operation became ready because a message can be sent or received.
+    Operation(Operation),
+}
+
+impl From<usize> for Selected {
+    #[inline]
+    fn from(val: usize) -> Selected {
+        match val {
+            0 => Selected::Waiting,
+            1 => Selected::Aborted,
+            2 => Selected::Disconnected,
+            oper => Selected::Operation(Operation(oper)),
+        }
+    }
+}
+
+impl Into<usize> for Selected {
+    #[inline]
+    fn into(self) -> usize {
+        match self {
+            Selected::Waiting => 0,
+            Selected::Aborted => 1,
+            Selected::Disconnected => 2,
+            Selected::Operation(Operation(val)) => val,
+        }
+    }
+}
diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs
new file mode 100644
index 00000000000..d0904b4b94c
--- /dev/null
+++ b/library/std/src/sync/mpmc/utils.rs
@@ -0,0 +1,144 @@
+use crate::cell::Cell;
+use crate::ops::{Deref, DerefMut};
+
+/// Pads and aligns a value to the length of a cache line.
+#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
+// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
+// lines at a time, so we have to align to 128 bytes rather than 64.
+//
+// Sources:
+// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
+// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
+//
+// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size.
+//
+// Sources:
+// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
+//
+// powerpc64 has 128-byte cache line size.
+//
+// Sources:
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9
+#[cfg_attr(
+    any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64",),
+    repr(align(128))
+)]
+// arm, mips, mips64, and riscv64 have 32-byte cache line size.
+//
+// Sources:
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_riscv64.go#L7
+#[cfg_attr(
+    any(
+        target_arch = "arm",
+        target_arch = "mips",
+        target_arch = "mips64",
+        target_arch = "riscv64",
+    ),
+    repr(align(32))
+)]
+// s390x has 256-byte cache line size.
+//
+// Sources:
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7
+#[cfg_attr(target_arch = "s390x", repr(align(256)))]
+// x86 and wasm have 64-byte cache line size.
+//
+// Sources:
+// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9
+// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7
+//
+// All others are assumed to have 64-byte cache line size.
+#[cfg_attr(
+    not(any(
+        target_arch = "x86_64",
+        target_arch = "aarch64",
+        target_arch = "powerpc64",
+        target_arch = "arm",
+        target_arch = "mips",
+        target_arch = "mips64",
+        target_arch = "riscv64",
+        target_arch = "s390x",
+    )),
+    repr(align(64))
+)]
+pub struct CachePadded<T> {
+    value: T,
+}
+
+impl<T> CachePadded<T> {
+    /// Pads and aligns a value to the length of a cache line.
+    pub fn new(value: T) -> CachePadded<T> {
+        CachePadded::<T> { value }
+    }
+}
+
+impl<T> Deref for CachePadded<T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        &self.value
+    }
+}
+
+impl<T> DerefMut for CachePadded<T> {
+    fn deref_mut(&mut self) -> &mut T {
+        &mut self.value
+    }
+}
+
+const SPIN_LIMIT: u32 = 6;
+const YIELD_LIMIT: u32 = 10;
+
+/// Performs exponential backoff in spin loops.
+pub struct Backoff {
+    step: Cell<u32>,
+}
+
+impl Backoff {
+    /// Creates a new `Backoff`.
+    pub fn new() -> Self {
+        Backoff { step: Cell::new(0) }
+    }
+
+    /// Backs off in a lock-free loop.
+    ///
+    /// This method should be used when we need to retry an operation because another thread made
+    /// progress.
+    #[inline]
+    pub fn spin(&self) {
+        let step = self.step.get().min(SPIN_LIMIT);
+        for _ in 0..step.pow(2) {
+            crate::hint::spin_loop();
+        }
+
+        if self.step.get() <= SPIN_LIMIT {
+            self.step.set(self.step.get() + 1);
+        }
+    }
+
+    /// Backs off in a blocking loop.
+    #[inline]
+    pub fn snooze(&self) {
+        if self.step.get() <= SPIN_LIMIT {
+            for _ in 0..self.step.get().pow(2) {
+                crate::hint::spin_loop()
+            }
+        } else {
+            crate::thread::yield_now();
+        }
+
+        if self.step.get() <= YIELD_LIMIT {
+            self.step.set(self.step.get() + 1);
+        }
+    }
+
+    /// Returns `true` if exponential backoff has completed and blocking the thread is advised.
+    #[inline]
+    pub fn is_completed(&self) -> bool {
+        self.step.get() > YIELD_LIMIT
+    }
+}
diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs
new file mode 100644
index 00000000000..4912ca4f815
--- /dev/null
+++ b/library/std/src/sync/mpmc/waker.rs
@@ -0,0 +1,204 @@
+//! Waking mechanism for threads blocked on channel operations.
+
+use super::context::Context;
+use super::select::{Operation, Selected};
+
+use crate::ptr;
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::Mutex;
+
+/// Represents a thread blocked on a specific channel operation.
+pub(crate) struct Entry {
+    /// The operation.
+    pub(crate) oper: Operation,
+
+    /// Optional packet.
+    pub(crate) packet: *mut (),
+
+    /// Context associated with the thread owning this operation.
+    pub(crate) cx: Context,
+}
+
+/// A queue of threads blocked on channel operations.
+///
+/// This data structure is used by threads to register blocking operations and get woken up once
+/// an operation becomes ready.
+pub(crate) struct Waker {
+    /// A list of select operations.
+    selectors: Vec<Entry>,
+
+    /// A list of operations waiting to be ready.
+    observers: Vec<Entry>,
+}
+
+impl Waker {
+    /// Creates a new `Waker`.
+    #[inline]
+    pub(crate) fn new() -> Self {
+        Waker { selectors: Vec::new(), observers: Vec::new() }
+    }
+
+    /// Registers a select operation.
+    #[inline]
+    pub(crate) fn register(&mut self, oper: Operation, cx: &Context) {
+        self.register_with_packet(oper, ptr::null_mut(), cx);
+    }
+
+    /// Registers a select operation and a packet.
+    #[inline]
+    pub(crate) fn register_with_packet(&mut self, oper: Operation, packet: *mut (), cx: &Context) {
+        self.selectors.push(Entry { oper, packet, cx: cx.clone() });
+    }
+
+    /// Unregisters a select operation.
+    #[inline]
+    pub(crate) fn unregister(&mut self, oper: Operation) -> Option<Entry> {
+        if let Some((i, _)) =
+            self.selectors.iter().enumerate().find(|&(_, entry)| entry.oper == oper)
+        {
+            let entry = self.selectors.remove(i);
+            Some(entry)
+        } else {
+            None
+        }
+    }
+
+    /// Attempts to find another thread's entry, select the operation, and wake it up.
+    #[inline]
+    pub(crate) fn try_select(&mut self) -> Option<Entry> {
+        self.selectors
+            .iter()
+            .position(|selector| {
+                // Does the entry belong to a different thread?
+                selector.cx.thread_id() != current_thread_id()
+                    && selector // Try selecting this operation.
+                        .cx
+                        .try_select(Selected::Operation(selector.oper))
+                        .is_ok()
+                    && {
+                        // Provide the packet.
+                        selector.cx.store_packet(selector.packet);
+                        // Wake the thread up.
+                        selector.cx.unpark();
+                        true
+                    }
+            })
+            // Remove the entry from the queue to keep it clean and improve
+            // performance.
+            .map(|pos| self.selectors.remove(pos))
+    }
+
+    /// Notifies all operations waiting to be ready.
+    #[inline]
+    pub(crate) fn notify(&mut self) {
+        for entry in self.observers.drain(..) {
+            if entry.cx.try_select(Selected::Operation(entry.oper)).is_ok() {
+                entry.cx.unpark();
+            }
+        }
+    }
+
+    /// Notifies all registered operations that the channel is disconnected.
+    #[inline]
+    pub(crate) fn disconnect(&mut self) {
+        for entry in self.selectors.iter() {
+            if entry.cx.try_select(Selected::Disconnected).is_ok() {
+                // Wake the thread up.
+                //
+                // Here we don't remove the entry from the queue. Registered threads must
+                // unregister from the waker by themselves. They might also want to recover the
+                // packet value and destroy it, if necessary.
+                entry.cx.unpark();
+            }
+        }
+
+        self.notify();
+    }
+}
+
+impl Drop for Waker {
+    #[inline]
+    fn drop(&mut self) {
+        debug_assert_eq!(self.selectors.len(), 0);
+        debug_assert_eq!(self.observers.len(), 0);
+    }
+}
+
+/// A waker that can be shared among threads without locking.
+///
+/// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization.
+pub(crate) struct SyncWaker {
+    /// The inner `Waker`.
+    inner: Mutex<Waker>,
+
+    /// `true` if the waker is empty.
+    is_empty: AtomicBool,
+}
+
+impl SyncWaker {
+    /// Creates a new `SyncWaker`.
+    #[inline]
+    pub(crate) fn new() -> Self {
+        SyncWaker { inner: Mutex::new(Waker::new()), is_empty: AtomicBool::new(true) }
+    }
+
+    /// Registers the current thread with an operation.
+    #[inline]
+    pub(crate) fn register(&self, oper: Operation, cx: &Context) {
+        let mut inner = self.inner.lock().unwrap();
+        inner.register(oper, cx);
+        self.is_empty
+            .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
+    }
+
+    /// Unregisters an operation previously registered by the current thread.
+    #[inline]
+    pub(crate) fn unregister(&self, oper: Operation) -> Option<Entry> {
+        let mut inner = self.inner.lock().unwrap();
+        let entry = inner.unregister(oper);
+        self.is_empty
+            .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
+        entry
+    }
+
+    /// Attempts to find one thread (not the current one), select its operation, and wake it up.
+    #[inline]
+    pub(crate) fn notify(&self) {
+        if !self.is_empty.load(Ordering::SeqCst) {
+            let mut inner = self.inner.lock().unwrap();
+            if !self.is_empty.load(Ordering::SeqCst) {
+                inner.try_select();
+                inner.notify();
+                self.is_empty.store(
+                    inner.selectors.is_empty() && inner.observers.is_empty(),
+                    Ordering::SeqCst,
+                );
+            }
+        }
+    }
+
+    /// Notifies all threads that the channel is disconnected.
+    #[inline]
+    pub(crate) fn disconnect(&self) {
+        let mut inner = self.inner.lock().unwrap();
+        inner.disconnect();
+        self.is_empty
+            .store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
+    }
+}
+
+impl Drop for SyncWaker {
+    #[inline]
+    fn drop(&mut self) {
+        debug_assert!(self.is_empty.load(Ordering::SeqCst));
+    }
+}
+
+/// Returns a unique id for the current thread.
+#[inline]
+pub fn current_thread_id() -> usize {
+    // `u8` is not drop so this variable will be available during thread destruction,
+    // whereas `thread::current()` would not be
+    thread_local! { static DUMMY: u8 = 0 }
+    DUMMY.with(|x| (x as *const u8).addr())
+}
diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs
new file mode 100644
index 00000000000..fccd6c29a7e
--- /dev/null
+++ b/library/std/src/sync/mpmc/zero.rs
@@ -0,0 +1,318 @@
+//! Zero-capacity channel.
+//!
+//! This kind of channel is also known as *rendezvous* channel.
+
+use super::context::Context;
+use super::error::*;
+use super::select::{Operation, Selected, Token};
+use super::utils::Backoff;
+use super::waker::Waker;
+
+use crate::cell::UnsafeCell;
+use crate::marker::PhantomData;
+use crate::sync::atomic::{AtomicBool, Ordering};
+use crate::sync::Mutex;
+use crate::time::Instant;
+use crate::{fmt, ptr};
+
+/// A pointer to a packet.
+pub(crate) struct ZeroToken(*mut ());
+
+impl Default for ZeroToken {
+    fn default() -> Self {
+        Self(ptr::null_mut())
+    }
+}
+
+impl fmt::Debug for ZeroToken {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(&(self.0 as usize), f)
+    }
+}
+
+/// A slot for passing one message from a sender to a receiver.
+struct Packet<T> {
+    /// Equals `true` if the packet is allocated on the stack.
+    on_stack: bool,
+
+    /// Equals `true` once the packet is ready for reading or writing.
+    ready: AtomicBool,
+
+    /// The message.
+    msg: UnsafeCell<Option<T>>,
+}
+
+impl<T> Packet<T> {
+    /// Creates an empty packet on the stack.
+    fn empty_on_stack() -> Packet<T> {
+        Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(None) }
+    }
+
+    /// Creates a packet on the stack, containing a message.
+    fn message_on_stack(msg: T) -> Packet<T> {
+        Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(Some(msg)) }
+    }
+
+    /// Waits until the packet becomes ready for reading or writing.
+    fn wait_ready(&self) {
+        let backoff = Backoff::new();
+        while !self.ready.load(Ordering::Acquire) {
+            backoff.snooze();
+        }
+    }
+}
+
+/// Inner representation of a zero-capacity channel.
+struct Inner {
+    /// Senders waiting to pair up with a receive operation.
+    senders: Waker,
+
+    /// Receivers waiting to pair up with a send operation.
+    receivers: Waker,
+
+    /// Equals `true` when the channel is disconnected.
+    is_disconnected: bool,
+}
+
+/// Zero-capacity channel.
+pub(crate) struct Channel<T> {
+    /// Inner representation of the channel.
+    inner: Mutex<Inner>,
+
+    /// Indicates that dropping a `Channel<T>` may drop values of type `T`.
+    _marker: PhantomData<T>,
+}
+
+impl<T> Channel<T> {
+    /// Constructs a new zero-capacity channel.
+    pub(crate) fn new() -> Self {
+        Channel {
+            inner: Mutex::new(Inner {
+                senders: Waker::new(),
+                receivers: Waker::new(),
+                is_disconnected: false,
+            }),
+            _marker: PhantomData,
+        }
+    }
+
+    /// Writes a message into the packet.
+    pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
+        // If there is no packet, the channel is disconnected.
+        if token.zero.0.is_null() {
+            return Err(msg);
+        }
+
+        let packet = &*(token.zero.0 as *const Packet<T>);
+        packet.msg.get().write(Some(msg));
+        packet.ready.store(true, Ordering::Release);
+        Ok(())
+    }
+
+    /// Reads a message from the packet.
+    pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
+        // If there is no packet, the channel is disconnected.
+        if token.zero.0.is_null() {
+            return Err(());
+        }
+
+        let packet = &*(token.zero.0 as *const Packet<T>);
+
+        if packet.on_stack {
+            // The message has been in the packet from the beginning, so there is no need to wait
+            // for it. However, after reading the message, we need to set `ready` to `true` in
+            // order to signal that the packet can be destroyed.
+            let msg = packet.msg.get().replace(None).unwrap();
+            packet.ready.store(true, Ordering::Release);
+            Ok(msg)
+        } else {
+            // Wait until the message becomes available, then read it and destroy the
+            // heap-allocated packet.
+            packet.wait_ready();
+            let msg = packet.msg.get().replace(None).unwrap();
+            drop(Box::from_raw(token.zero.0 as *mut Packet<T>));
+            Ok(msg)
+        }
+    }
+
+    /// Attempts to send a message into the channel.
+    pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
+        let token = &mut Token::default();
+        let mut inner = self.inner.lock().unwrap();
+
+        // If there's a waiting receiver, pair up with it.
+        if let Some(operation) = inner.receivers.try_select() {
+            token.zero.0 = operation.packet;
+            drop(inner);
+            unsafe {
+                self.write(token, msg).ok().unwrap();
+            }
+            Ok(())
+        } else if inner.is_disconnected {
+            Err(TrySendError::Disconnected(msg))
+        } else {
+            Err(TrySendError::Full(msg))
+        }
+    }
+
+    /// Sends a message into the channel.
+    pub(crate) fn send(
+        &self,
+        msg: T,
+        deadline: Option<Instant>,
+    ) -> Result<(), SendTimeoutError<T>> {
+        let token = &mut Token::default();
+        let mut inner = self.inner.lock().unwrap();
+
+        // If there's a waiting receiver, pair up with it.
+        if let Some(operation) = inner.receivers.try_select() {
+            token.zero.0 = operation.packet;
+            drop(inner);
+            unsafe {
+                self.write(token, msg).ok().unwrap();
+            }
+            return Ok(());
+        }
+
+        if inner.is_disconnected {
+            return Err(SendTimeoutError::Disconnected(msg));
+        }
+
+        Context::with(|cx| {
+            // Prepare for blocking until a receiver wakes us up.
+            let oper = Operation::hook(token);
+            let mut packet = Packet::<T>::message_on_stack(msg);
+            inner.senders.register_with_packet(oper, &mut packet as *mut Packet<T> as *mut (), cx);
+            inner.receivers.notify();
+            drop(inner);
+
+            // Block the current thread.
+            let sel = cx.wait_until(deadline);
+
+            match sel {
+                Selected::Waiting => unreachable!(),
+                Selected::Aborted => {
+                    self.inner.lock().unwrap().senders.unregister(oper).unwrap();
+                    let msg = unsafe { packet.msg.get().replace(None).unwrap() };
+                    Err(SendTimeoutError::Timeout(msg))
+                }
+                Selected::Disconnected => {
+                    self.inner.lock().unwrap().senders.unregister(oper).unwrap();
+                    let msg = unsafe { packet.msg.get().replace(None).unwrap() };
+                    Err(SendTimeoutError::Disconnected(msg))
+                }
+                Selected::Operation(_) => {
+                    // Wait until the message is read, then drop the packet.
+                    packet.wait_ready();
+                    Ok(())
+                }
+            }
+        })
+    }
+
+    /// Attempts to receive a message without blocking.
+    pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
+        let token = &mut Token::default();
+        let mut inner = self.inner.lock().unwrap();
+
+        // If there's a waiting sender, pair up with it.
+        if let Some(operation) = inner.senders.try_select() {
+            token.zero.0 = operation.packet;
+            drop(inner);
+            unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
+        } else if inner.is_disconnected {
+            Err(TryRecvError::Disconnected)
+        } else {
+            Err(TryRecvError::Empty)
+        }
+    }
+
+    /// Receives a message from the channel.
+    pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
+        let token = &mut Token::default();
+        let mut inner = self.inner.lock().unwrap();
+
+        // If there's a waiting sender, pair up with it.
+        if let Some(operation) = inner.senders.try_select() {
+            token.zero.0 = operation.packet;
+            drop(inner);
+            unsafe {
+                return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
+            }
+        }
+
+        if inner.is_disconnected {
+            return Err(RecvTimeoutError::Disconnected);
+        }
+
+        Context::with(|cx| {
+            // Prepare for blocking until a sender wakes us up.
+            let oper = Operation::hook(token);
+            let mut packet = Packet::<T>::empty_on_stack();
+            inner.receivers.register_with_packet(
+                oper,
+                &mut packet as *mut Packet<T> as *mut (),
+                cx,
+            );
+            inner.senders.notify();
+            drop(inner);
+
+            // Block the current thread.
+            let sel = cx.wait_until(deadline);
+
+            match sel {
+                Selected::Waiting => unreachable!(),
+                Selected::Aborted => {
+                    self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
+                    Err(RecvTimeoutError::Timeout)
+                }
+                Selected::Disconnected => {
+                    self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
+                    Err(RecvTimeoutError::Disconnected)
+                }
+                Selected::Operation(_) => {
+                    // Wait until the message is provided, then read it.
+                    packet.wait_ready();
+                    unsafe { Ok(packet.msg.get().replace(None).unwrap()) }
+                }
+            }
+        })
+    }
+
+    /// Disconnects the channel and wakes up all blocked senders and receivers.
+    ///
+    /// Returns `true` if this call disconnected the channel.
+    pub(crate) fn disconnect(&self) -> bool {
+        let mut inner = self.inner.lock().unwrap();
+
+        if !inner.is_disconnected {
+            inner.is_disconnected = true;
+            inner.senders.disconnect();
+            inner.receivers.disconnect();
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Returns the current number of messages inside the channel.
+    pub(crate) fn len(&self) -> usize {
+        0
+    }
+
+    /// Returns the capacity of the channel.
+    #[allow(clippy::unnecessary_wraps)] // This is intentional.
+    pub(crate) fn capacity(&self) -> Option<usize> {
+        Some(0)
+    }
+
+    /// Returns `true` if the channel is empty.
+    pub(crate) fn is_empty(&self) -> bool {
+        true
+    }
+
+    /// Returns `true` if the channel is full.
+    pub(crate) fn is_full(&self) -> bool {
+        true
+    }
+}
diff --git a/library/std/src/sync/mpsc/blocking.rs b/library/std/src/sync/mpsc/blocking.rs
deleted file mode 100644
index 021df7b096c..00000000000
--- a/library/std/src/sync/mpsc/blocking.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-//! Generic support for building blocking abstractions.
-
-use crate::sync::atomic::{AtomicBool, Ordering};
-use crate::sync::Arc;
-use crate::thread::{self, Thread};
-use crate::time::Instant;
-
-struct Inner {
-    thread: Thread,
-    woken: AtomicBool,
-}
-
-unsafe impl Send for Inner {}
-unsafe impl Sync for Inner {}
-
-#[derive(Clone)]
-pub struct SignalToken {
-    inner: Arc<Inner>,
-}
-
-pub struct WaitToken {
-    inner: Arc<Inner>,
-}
-
-impl !Send for WaitToken {}
-
-impl !Sync for WaitToken {}
-
-pub fn tokens() -> (WaitToken, SignalToken) {
-    let inner = Arc::new(Inner { thread: thread::current(), woken: AtomicBool::new(false) });
-    let wait_token = WaitToken { inner: inner.clone() };
-    let signal_token = SignalToken { inner };
-    (wait_token, signal_token)
-}
-
-impl SignalToken {
-    pub fn signal(&self) -> bool {
-        let wake = self
-            .inner
-            .woken
-            .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
-            .is_ok();
-        if wake {
-            self.inner.thread.unpark();
-        }
-        wake
-    }
-
-    /// Converts to an unsafe raw pointer. Useful for storing in a pipe's state
-    /// flag.
-    #[inline]
-    pub unsafe fn to_raw(self) -> *mut u8 {
-        Arc::into_raw(self.inner) as *mut u8
-    }
-
-    /// Converts from an unsafe raw pointer. Useful for retrieving a pipe's state
-    /// flag.
-    #[inline]
-    pub unsafe fn from_raw(signal_ptr: *mut u8) -> SignalToken {
-        SignalToken { inner: Arc::from_raw(signal_ptr as *mut Inner) }
-    }
-}
-
-impl WaitToken {
-    pub fn wait(self) {
-        while !self.inner.woken.load(Ordering::SeqCst) {
-            thread::park()
-        }
-    }
-
-    /// Returns `true` if we wake up normally.
-    pub fn wait_max_until(self, end: Instant) -> bool {
-        while !self.inner.woken.load(Ordering::SeqCst) {
-            let now = Instant::now();
-            if now >= end {
-                return false;
-            }
-            thread::park_timeout(end - now)
-        }
-        true
-    }
-}
diff --git a/library/std/src/sync/mpsc/cache_aligned.rs b/library/std/src/sync/mpsc/cache_aligned.rs
deleted file mode 100644
index 9197f0d6e6c..00000000000
--- a/library/std/src/sync/mpsc/cache_aligned.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use crate::ops::{Deref, DerefMut};
-
-#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(target_arch = "aarch64", repr(align(128)))]
-#[cfg_attr(not(target_arch = "aarch64"), repr(align(64)))]
-pub(super) struct CacheAligned<T>(pub T);
-
-impl<T> Deref for CacheAligned<T> {
-    type Target = T;
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl<T> DerefMut for CacheAligned<T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl<T> CacheAligned<T> {
-    pub(super) fn new(t: T) -> Self {
-        CacheAligned(t)
-    }
-}
diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs
index e85a8723965..27fba761ada 100644
--- a/library/std/src/sync/mpsc/mod.rs
+++ b/library/std/src/sync/mpsc/mod.rs
@@ -143,175 +143,16 @@ mod tests;
 #[cfg(all(test, not(target_os = "emscripten")))]
 mod sync_tests;
 
-// A description of how Rust's channel implementation works
-//
-// Channels are supposed to be the basic building block for all other
-// concurrent primitives that are used in Rust. As a result, the channel type
-// needs to be highly optimized, flexible, and broad enough for use everywhere.
-//
-// The choice of implementation of all channels is to be built on lock-free data
-// structures. The channels themselves are then consequently also lock-free data
-// structures. As always with lock-free code, this is a very "here be dragons"
-// territory, especially because I'm unaware of any academic papers that have
-// gone into great length about channels of these flavors.
-//
-// ## Flavors of channels
-//
-// From the perspective of a consumer of this library, there is only one flavor
-// of channel. This channel can be used as a stream and cloned to allow multiple
-// senders. Under the hood, however, there are actually three flavors of
-// channels in play.
-//
-// * Flavor::Oneshots - these channels are highly optimized for the one-send use
-//                      case. They contain as few atomics as possible and
-//                      involve one and exactly one allocation.
-// * Streams - these channels are optimized for the non-shared use case. They
-//             use a different concurrent queue that is more tailored for this
-//             use case. The initial allocation of this flavor of channel is not
-//             optimized.
-// * Shared - this is the most general form of channel that this module offers,
-//            a channel with multiple senders. This type is as optimized as it
-//            can be, but the previous two types mentioned are much faster for
-//            their use-cases.
-//
-// ## Concurrent queues
-//
-// The basic idea of Rust's Sender/Receiver types is that send() never blocks,
-// but recv() obviously blocks. This means that under the hood there must be
-// some shared and concurrent queue holding all of the actual data.
-//
-// With two flavors of channels, two flavors of queues are also used. We have
-// chosen to use queues from a well-known author that are abbreviated as SPSC
-// and MPSC (single producer, single consumer and multiple producer, single
-// consumer). SPSC queues are used for streams while MPSC queues are used for
-// shared channels.
-//
-// ### SPSC optimizations
-//
-// The SPSC queue found online is essentially a linked list of nodes where one
-// half of the nodes are the "queue of data" and the other half of nodes are a
-// cache of unused nodes. The unused nodes are used such that an allocation is
-// not required on every push() and a free doesn't need to happen on every
-// pop().
-//
-// As found online, however, the cache of nodes is of an infinite size. This
-// means that if a channel at one point in its life had 50k items in the queue,
-// then the queue will always have the capacity for 50k items. I believed that
-// this was an unnecessary limitation of the implementation, so I have altered
-// the queue to optionally have a bound on the cache size.
-//
-// By default, streams will have an unbounded SPSC queue with a small-ish cache
-// size. The hope is that the cache is still large enough to have very fast
-// send() operations while not too large such that millions of channels can
-// coexist at once.
-//
-// ### MPSC optimizations
-//
-// Right now the MPSC queue has not been optimized. Like the SPSC queue, it uses
-// a linked list under the hood to earn its unboundedness, but I have not put
-// forth much effort into having a cache of nodes similar to the SPSC queue.
-//
-// For now, I believe that this is "ok" because shared channels are not the most
-// common type, but soon we may wish to revisit this queue choice and determine
-// another candidate for backend storage of shared channels.
-//
-// ## Overview of the Implementation
-//
-// Now that there's a little background on the concurrent queues used, it's
-// worth going into much more detail about the channels themselves. The basic
-// pseudocode for a send/recv are:
-//
-//
-//      send(t)                             recv()
-//        queue.push(t)                       return if queue.pop()
-//        if increment() == -1                deschedule {
-//          wakeup()                            if decrement() > 0
-//                                                cancel_deschedule()
-//                                            }
-//                                            queue.pop()
-//
-// As mentioned before, there are no locks in this implementation, only atomic
-// instructions are used.
-//
-// ### The internal atomic counter
-//
-// Every channel has a shared counter with each half to keep track of the size
-// of the queue. This counter is used to abort descheduling by the receiver and
-// to know when to wake up on the sending side.
-//
-// As seen in the pseudocode, senders will increment this count and receivers
-// will decrement the count. The theory behind this is that if a sender sees a
-// -1 count, it will wake up the receiver, and if the receiver sees a 1+ count,
-// then it doesn't need to block.
-//
-// The recv() method has a beginning call to pop(), and if successful, it needs
-// to decrement the count. It is a crucial implementation detail that this
-// decrement does *not* happen to the shared counter. If this were the case,
-// then it would be possible for the counter to be very negative when there were
-// no receivers waiting, in which case the senders would have to determine when
-// it was actually appropriate to wake up a receiver.
-//
-// Instead, the "steal count" is kept track of separately (not atomically
-// because it's only used by receivers), and then the decrement() call when
-// descheduling will lump in all of the recent steals into one large decrement.
-//
-// The implication of this is that if a sender sees a -1 count, then there's
-// guaranteed to be a waiter waiting!
-//
-// ## Native Implementation
-//
-// A major goal of these channels is to work seamlessly on and off the runtime.
-// All of the previous race conditions have been worded in terms of
-// scheduler-isms (which is obviously not available without the runtime).
-//
-// For now, native usage of channels (off the runtime) will fall back onto
-// mutexes/cond vars for descheduling/atomic decisions. The no-contention path
-// is still entirely lock-free, the "deschedule" blocks above are surrounded by
-// a mutex and the "wakeup" blocks involve grabbing a mutex and signaling on a
-// condition variable.
-//
-// ## Select
-//
-// Being able to support selection over channels has greatly influenced this
-// design, and not only does selection need to work inside the runtime, but also
-// outside the runtime.
-//
-// The implementation is fairly straightforward. The goal of select() is not to
-// return some data, but only to return which channel can receive data without
-// blocking. The implementation is essentially the entire blocking procedure
-// followed by an increment as soon as its woken up. The cancellation procedure
-// involves an increment and swapping out of to_wake to acquire ownership of the
-// thread to unblock.
-//
-// Sadly this current implementation requires multiple allocations, so I have
-// seen the throughput of select() be much worse than it should be. I do not
-// believe that there is anything fundamental that needs to change about these
-// channels, however, in order to support a more efficient select().
-//
-// FIXME: Select is now removed, so these factors are ready to be cleaned up!
-//
-// # Conclusion
-//
-// And now that you've seen all the races that I found and attempted to fix,
-// here's the code for you to find some more!
-
-use crate::cell::UnsafeCell;
+// MPSC channels are built as a wrapper around MPMC channels, which
+// were ported from the `crossbeam-channel` crate. MPMC channels are
+// not exposed publicly, but if you are curious about the implementation,
+// that's where everything is.
+
 use crate::error;
 use crate::fmt;
-use crate::mem;
-use crate::sync::Arc;
+use crate::sync::mpmc;
 use crate::time::{Duration, Instant};
 
-mod blocking;
-mod mpsc_queue;
-mod oneshot;
-mod shared;
-mod spsc_queue;
-mod stream;
-mod sync;
-
-mod cache_aligned;
-
 /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type.
 /// This half can only be owned by one thread.
 ///
@@ -341,7 +182,7 @@ mod cache_aligned;
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "Receiver")]
 pub struct Receiver<T> {
-    inner: UnsafeCell<Flavor<T>>,
+    inner: mpmc::Receiver<T>,
 }
 
 // The receiver port can be sent from place to place, so long as it
@@ -498,7 +339,7 @@ pub struct IntoIter<T> {
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Sender<T> {
-    inner: UnsafeCell<Flavor<T>>,
+    inner: mpmc::Sender<T>,
 }
 
 // The send port can be sent from place to place, so long as it
@@ -557,7 +398,7 @@ impl<T> !Sync for Sender<T> {}
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct SyncSender<T> {
-    inner: Arc<sync::Packet<T>>,
+    inner: mpmc::Sender<T>,
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -643,34 +484,6 @@ pub enum TrySendError<T> {
     Disconnected(#[stable(feature = "rust1", since = "1.0.0")] T),
 }
 
-enum Flavor<T> {
-    Oneshot(Arc<oneshot::Packet<T>>),
-    Stream(Arc<stream::Packet<T>>),
-    Shared(Arc<shared::Packet<T>>),
-    Sync(Arc<sync::Packet<T>>),
-}
-
-#[doc(hidden)]
-trait UnsafeFlavor<T> {
-    fn inner_unsafe(&self) -> &UnsafeCell<Flavor<T>>;
-    unsafe fn inner_mut(&self) -> &mut Flavor<T> {
-        &mut *self.inner_unsafe().get()
-    }
-    unsafe fn inner(&self) -> &Flavor<T> {
-        &*self.inner_unsafe().get()
-    }
-}
-impl<T> UnsafeFlavor<T> for Sender<T> {
-    fn inner_unsafe(&self) -> &UnsafeCell<Flavor<T>> {
-        &self.inner
-    }
-}
-impl<T> UnsafeFlavor<T> for Receiver<T> {
-    fn inner_unsafe(&self) -> &UnsafeCell<Flavor<T>> {
-        &self.inner
-    }
-}
-
 /// Creates a new asynchronous channel, returning the sender/receiver halves.
 /// All data sent on the [`Sender`] will become available on the [`Receiver`] in
 /// the same order as it was sent, and no [`send`] will block the calling thread
@@ -711,8 +524,8 @@ impl<T> UnsafeFlavor<T> for Receiver<T> {
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
-    let a = Arc::new(oneshot::Packet::new());
-    (Sender::new(Flavor::Oneshot(a.clone())), Receiver::new(Flavor::Oneshot(a)))
+    let (tx, rx) = mpmc::channel();
+    (Sender { inner: tx }, Receiver { inner: rx })
 }
 
 /// Creates a new synchronous, bounded channel.
@@ -760,8 +573,8 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
 #[must_use]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Receiver<T>) {
-    let a = Arc::new(sync::Packet::new(bound));
-    (SyncSender::new(a.clone()), Receiver::new(Flavor::Sync(a)))
+    let (tx, rx) = mpmc::sync_channel(bound);
+    (SyncSender { inner: tx }, Receiver { inner: rx })
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -769,10 +582,6 @@ pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Receiver<T>) {
 ////////////////////////////////////////////////////////////////////////////////
 
 impl<T> Sender<T> {
-    fn new(inner: Flavor<T>) -> Sender<T> {
-        Sender { inner: UnsafeCell::new(inner) }
-    }
-
     /// Attempts to send a value on this channel, returning it back if it could
     /// not be sent.
     ///
@@ -802,40 +611,7 @@ impl<T> Sender<T> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn send(&self, t: T) -> Result<(), SendError<T>> {
-        let (new_inner, ret) = match *unsafe { self.inner() } {
-            Flavor::Oneshot(ref p) => {
-                if !p.sent() {
-                    return p.send(t).map_err(SendError);
-                } else {
-                    let a = Arc::new(stream::Packet::new());
-                    let rx = Receiver::new(Flavor::Stream(a.clone()));
-                    match p.upgrade(rx) {
-                        oneshot::UpSuccess => {
-                            let ret = a.send(t);
-                            (a, ret)
-                        }
-                        oneshot::UpDisconnected => (a, Err(t)),
-                        oneshot::UpWoke(token) => {
-                            // This send cannot panic because the thread is
-                            // asleep (we're looking at it), so the receiver
-                            // can't go away.
-                            a.send(t).ok().unwrap();
-                            token.signal();
-                            (a, Ok(()))
-                        }
-                    }
-                }
-            }
-            Flavor::Stream(ref p) => return p.send(t).map_err(SendError),
-            Flavor::Shared(ref p) => return p.send(t).map_err(SendError),
-            Flavor::Sync(..) => unreachable!(),
-        };
-
-        unsafe {
-            let tmp = Sender::new(Flavor::Stream(new_inner));
-            mem::swap(self.inner_mut(), tmp.inner_mut());
-        }
-        ret.map_err(SendError)
+        self.inner.send(t)
     }
 }
 
@@ -847,57 +623,14 @@ impl<T> Clone for Sender<T> {
     /// (including the original) need to be dropped in order for
     /// [`Receiver::recv`] to stop blocking.
     fn clone(&self) -> Sender<T> {
-        let packet = match *unsafe { self.inner() } {
-            Flavor::Oneshot(ref p) => {
-                let a = Arc::new(shared::Packet::new());
-                {
-                    let guard = a.postinit_lock();
-                    let rx = Receiver::new(Flavor::Shared(a.clone()));
-                    let sleeper = match p.upgrade(rx) {
-                        oneshot::UpSuccess | oneshot::UpDisconnected => None,
-                        oneshot::UpWoke(task) => Some(task),
-                    };
-                    a.inherit_blocker(sleeper, guard);
-                }
-                a
-            }
-            Flavor::Stream(ref p) => {
-                let a = Arc::new(shared::Packet::new());
-                {
-                    let guard = a.postinit_lock();
-                    let rx = Receiver::new(Flavor::Shared(a.clone()));
-                    let sleeper = match p.upgrade(rx) {
-                        stream::UpSuccess | stream::UpDisconnected => None,
-                        stream::UpWoke(task) => Some(task),
-                    };
-                    a.inherit_blocker(sleeper, guard);
-                }
-                a
-            }
-            Flavor::Shared(ref p) => {
-                p.clone_chan();
-                return Sender::new(Flavor::Shared(p.clone()));
-            }
-            Flavor::Sync(..) => unreachable!(),
-        };
-
-        unsafe {
-            let tmp = Sender::new(Flavor::Shared(packet.clone()));
-            mem::swap(self.inner_mut(), tmp.inner_mut());
-        }
-        Sender::new(Flavor::Shared(packet))
+        Sender { inner: self.inner.clone() }
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Drop for Sender<T> {
     fn drop(&mut self) {
-        match *unsafe { self.inner() } {
-            Flavor::Oneshot(ref p) => p.drop_chan(),
-            Flavor::Stream(ref p) => p.drop_chan(),
-            Flavor::Shared(ref p) => p.drop_chan(),
-            Flavor::Sync(..) => unreachable!(),
-        }
+        let _ = self.inner;
     }
 }
 
@@ -913,10 +646,6 @@ impl<T> fmt::Debug for Sender<T> {
 ////////////////////////////////////////////////////////////////////////////////
 
 impl<T> SyncSender<T> {
-    fn new(inner: Arc<sync::Packet<T>>) -> SyncSender<T> {
-        SyncSender { inner }
-    }
-
     /// Sends a value on this synchronous channel.
     ///
     /// This function will *block* until space in the internal buffer becomes
@@ -955,7 +684,7 @@ impl<T> SyncSender<T> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn send(&self, t: T) -> Result<(), SendError<T>> {
-        self.inner.send(t).map_err(SendError)
+        self.inner.send(t)
     }
 
     /// Attempts to send a value on this channel without blocking.
@@ -1016,15 +745,14 @@ impl<T> SyncSender<T> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Clone for SyncSender<T> {
     fn clone(&self) -> SyncSender<T> {
-        self.inner.clone_chan();
-        SyncSender::new(self.inner.clone())
+        SyncSender { inner: self.inner.clone() }
     }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Drop for SyncSender<T> {
     fn drop(&mut self) {
-        self.inner.drop_chan();
+        let _ = self.inner;
     }
 }
 
@@ -1040,10 +768,6 @@ impl<T> fmt::Debug for SyncSender<T> {
 ////////////////////////////////////////////////////////////////////////////////
 
 impl<T> Receiver<T> {
-    fn new(inner: Flavor<T>) -> Receiver<T> {
-        Receiver { inner: UnsafeCell::new(inner) }
-    }
-
     /// Attempts to return a pending value on this receiver without blocking.
     ///
     /// This method will never block the caller in order to wait for data to
@@ -1069,35 +793,7 @@ impl<T> Receiver<T> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn try_recv(&self) -> Result<T, TryRecvError> {
-        loop {
-            let new_port = match *unsafe { self.inner() } {
-                Flavor::Oneshot(ref p) => match p.try_recv() {
-                    Ok(t) => return Ok(t),
-                    Err(oneshot::Empty) => return Err(TryRecvError::Empty),
-                    Err(oneshot::Disconnected) => return Err(TryRecvError::Disconnected),
-                    Err(oneshot::Upgraded(rx)) => rx,
-                },
-                Flavor::Stream(ref p) => match p.try_recv() {
-                    Ok(t) => return Ok(t),
-                    Err(stream::Empty) => return Err(TryRecvError::Empty),
-                    Err(stream::Disconnected) => return Err(TryRecvError::Disconnected),
-                    Err(stream::Upgraded(rx)) => rx,
-                },
-                Flavor::Shared(ref p) => match p.try_recv() {
-                    Ok(t) => return Ok(t),
-                    Err(shared::Empty) => return Err(TryRecvError::Empty),
-                    Err(shared::Disconnected) => return Err(TryRecvError::Disconnected),
-                },
-                Flavor::Sync(ref p) => match p.try_recv() {
-                    Ok(t) => return Ok(t),
-                    Err(sync::Empty) => return Err(TryRecvError::Empty),
-                    Err(sync::Disconnected) => return Err(TryRecvError::Disconnected),
-                },
-            };
-            unsafe {
-                mem::swap(self.inner_mut(), new_port.inner_mut());
-            }
-        }
+        self.inner.try_recv()
     }
 
     /// Attempts to wait for a value on this receiver, returning an error if the
@@ -1156,31 +852,7 @@ impl<T> Receiver<T> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn recv(&self) -> Result<T, RecvError> {
-        loop {
-            let new_port = match *unsafe { self.inner() } {
-                Flavor::Oneshot(ref p) => match p.recv(None) {
-                    Ok(t) => return Ok(t),
-                    Err(oneshot::Disconnected) => return Err(RecvError),
-                    Err(oneshot::Upgraded(rx)) => rx,
-                    Err(oneshot::Empty) => unreachable!(),
-                },
-                Flavor::Stream(ref p) => match p.recv(None) {
-                    Ok(t) => return Ok(t),
-                    Err(stream::Disconnected) => return Err(RecvError),
-                    Err(stream::Upgraded(rx)) => rx,
-                    Err(stream::Empty) => unreachable!(),
-                },
-                Flavor::Shared(ref p) => match p.recv(None) {
-                    Ok(t) => return Ok(t),
-                    Err(shared::Disconnected) => return Err(RecvError),
-                    Err(shared::Empty) => unreachable!(),
-                },
-                Flavor::Sync(ref p) => return p.recv(None).map_err(|_| RecvError),
-            };
-            unsafe {
-                mem::swap(self.inner_mut(), new_port.inner_mut());
-            }
-        }
+        self.inner.recv()
     }
 
     /// Attempts to wait for a value on this receiver, returning an error if the
@@ -1198,34 +870,6 @@ impl<T> Receiver<T> {
     /// However, since channels are buffered, messages sent before the disconnect
     /// will still be properly received.
     ///
-    /// # Known Issues
-    ///
-    /// There is currently a known issue (see [`#39364`]) that causes `recv_timeout`
-    /// to panic unexpectedly with the following example:
-    ///
-    /// ```no_run
-    /// use std::sync::mpsc::channel;
-    /// use std::thread;
-    /// use std::time::Duration;
-    ///
-    /// let (tx, rx) = channel::<String>();
-    ///
-    /// thread::spawn(move || {
-    ///     let d = Duration::from_millis(10);
-    ///     loop {
-    ///         println!("recv");
-    ///         let _r = rx.recv_timeout(d);
-    ///     }
-    /// });
-    ///
-    /// thread::sleep(Duration::from_millis(100));
-    /// let _c1 = tx.clone();
-    ///
-    /// thread::sleep(Duration::from_secs(1));
-    /// ```
-    ///
-    /// [`#39364`]: https://github.com/rust-lang/rust/issues/39364
-    ///
     /// # Examples
     ///
     /// Successfully receiving value before encountering timeout:
@@ -1268,17 +912,7 @@ impl<T> Receiver<T> {
     /// ```
     #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")]
     pub fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
-        // Do an optimistic try_recv to avoid the performance impact of
-        // Instant::now() in the full-channel case.
-        match self.try_recv() {
-            Ok(result) => Ok(result),
-            Err(TryRecvError::Disconnected) => Err(RecvTimeoutError::Disconnected),
-            Err(TryRecvError::Empty) => match Instant::now().checked_add(timeout) {
-                Some(deadline) => self.recv_deadline(deadline),
-                // So far in the future that it's practically the same as waiting indefinitely.
-                None => self.recv().map_err(RecvTimeoutError::from),
-            },
-        }
+        self.inner.recv_timeout(timeout)
     }
 
     /// Attempts to wait for a value on this receiver, returning an error if the
@@ -1339,46 +973,7 @@ impl<T> Receiver<T> {
     /// ```
     #[unstable(feature = "deadline_api", issue = "46316")]
     pub fn recv_deadline(&self, deadline: Instant) -> Result<T, RecvTimeoutError> {
-        use self::RecvTimeoutError::*;
-
-        loop {
-            let port_or_empty = match *unsafe { self.inner() } {
-                Flavor::Oneshot(ref p) => match p.recv(Some(deadline)) {
-                    Ok(t) => return Ok(t),
-                    Err(oneshot::Disconnected) => return Err(Disconnected),
-                    Err(oneshot::Upgraded(rx)) => Some(rx),
-                    Err(oneshot::Empty) => None,
-                },
-                Flavor::Stream(ref p) => match p.recv(Some(deadline)) {
-                    Ok(t) => return Ok(t),
-                    Err(stream::Disconnected) => return Err(Disconnected),
-                    Err(stream::Upgraded(rx)) => Some(rx),
-                    Err(stream::Empty) => None,
-                },
-                Flavor::Shared(ref p) => match p.recv(Some(deadline)) {
-                    Ok(t) => return Ok(t),
-                    Err(shared::Disconnected) => return Err(Disconnected),
-                    Err(shared::Empty) => None,
-                },
-                Flavor::Sync(ref p) => match p.recv(Some(deadline)) {
-                    Ok(t) => return Ok(t),
-                    Err(sync::Disconnected) => return Err(Disconnected),
-                    Err(sync::Empty) => None,
-                },
-            };
-
-            if let Some(new_port) = port_or_empty {
-                unsafe {
-                    mem::swap(self.inner_mut(), new_port.inner_mut());
-                }
-            }
-
-            // If we're already passed the deadline, and we're here without
-            // data, return a timeout, else try again.
-            if Instant::now() >= deadline {
-                return Err(Timeout);
-            }
-        }
+        self.inner.recv_deadline(deadline)
     }
 
     /// Returns an iterator that will block waiting for messages, but never
@@ -1500,12 +1095,7 @@ impl<T> IntoIterator for Receiver<T> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Drop for Receiver<T> {
     fn drop(&mut self) {
-        match *unsafe { self.inner() } {
-            Flavor::Oneshot(ref p) => p.drop_port(),
-            Flavor::Stream(ref p) => p.drop_port(),
-            Flavor::Shared(ref p) => p.drop_port(),
-            Flavor::Sync(ref p) => p.drop_port(),
-        }
+        let _ = self.inner;
     }
 }
 
diff --git a/library/std/src/sync/mpsc/mpsc_queue.rs b/library/std/src/sync/mpsc/mpsc_queue.rs
deleted file mode 100644
index 7322512e3b4..00000000000
--- a/library/std/src/sync/mpsc/mpsc_queue.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-//! A mostly lock-free multi-producer, single consumer queue.
-//!
-//! This module contains an implementation of a concurrent MPSC queue. This
-//! queue can be used to share data between threads, and is also used as the
-//! building block of channels in rust.
-//!
-//! Note that the current implementation of this queue has a caveat of the `pop`
-//! method, and see the method for more information about it. Due to this
-//! caveat, this queue might not be appropriate for all use-cases.
-
-// The original implementation is based off:
-// https://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue
-//
-// Note that back when the code was imported, it was licensed under the BSD-2-Clause license:
-// http://web.archive.org/web/20110411011612/https://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue
-//
-// The original author of the code agreed to relicense it under `MIT OR Apache-2.0` in 2017, so as
-// of today the license of this file is the same as the rest of the codebase:
-// https://github.com/rust-lang/rust/pull/42149
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests;
-
-pub use self::PopResult::*;
-
-use core::cell::UnsafeCell;
-use core::ptr;
-
-use crate::boxed::Box;
-use crate::sync::atomic::{AtomicPtr, Ordering};
-
-/// A result of the `pop` function.
-pub enum PopResult<T> {
-    /// Some data has been popped
-    Data(T),
-    /// The queue is empty
-    Empty,
-    /// The queue is in an inconsistent state. Popping data should succeed, but
-    /// some pushers have yet to make enough progress in order allow a pop to
-    /// succeed. It is recommended that a pop() occur "in the near future" in
-    /// order to see if the sender has made progress or not
-    Inconsistent,
-}
-
-struct Node<T> {
-    next: AtomicPtr<Node<T>>,
-    value: Option<T>,
-}
-
-/// The multi-producer single-consumer structure. This is not cloneable, but it
-/// may be safely shared so long as it is guaranteed that there is only one
-/// popper at a time (many pushers are allowed).
-pub struct Queue<T> {
-    head: AtomicPtr<Node<T>>,
-    tail: UnsafeCell<*mut Node<T>>,
-}
-
-unsafe impl<T: Send> Send for Queue<T> {}
-unsafe impl<T: Send> Sync for Queue<T> {}
-
-impl<T> Node<T> {
-    unsafe fn new(v: Option<T>) -> *mut Node<T> {
-        Box::into_raw(box Node { next: AtomicPtr::new(ptr::null_mut()), value: v })
-    }
-}
-
-impl<T> Queue<T> {
-    /// Creates a new queue that is safe to share among multiple producers and
-    /// one consumer.
-    pub fn new() -> Queue<T> {
-        let stub = unsafe { Node::new(None) };
-        Queue { head: AtomicPtr::new(stub), tail: UnsafeCell::new(stub) }
-    }
-
-    /// Pushes a new value onto this queue.
-    pub fn push(&self, t: T) {
-        unsafe {
-            let n = Node::new(Some(t));
-            let prev = self.head.swap(n, Ordering::AcqRel);
-            (*prev).next.store(n, Ordering::Release);
-        }
-    }
-
-    /// Pops some data from this queue.
-    ///
-    /// Note that the current implementation means that this function cannot
-    /// return `Option<T>`. It is possible for this queue to be in an
-    /// inconsistent state where many pushes have succeeded and completely
-    /// finished, but pops cannot return `Some(t)`. This inconsistent state
-    /// happens when a pusher is pre-empted at an inopportune moment.
-    ///
-    /// This inconsistent state means that this queue does indeed have data, but
-    /// it does not currently have access to it at this time.
-    pub fn pop(&self) -> PopResult<T> {
-        unsafe {
-            let tail = *self.tail.get();
-            let next = (*tail).next.load(Ordering::Acquire);
-
-            if !next.is_null() {
-                *self.tail.get() = next;
-                assert!((*tail).value.is_none());
-                assert!((*next).value.is_some());
-                let ret = (*next).value.take().unwrap();
-                let _: Box<Node<T>> = Box::from_raw(tail);
-                return Data(ret);
-            }
-
-            if self.head.load(Ordering::Acquire) == tail { Empty } else { Inconsistent }
-        }
-    }
-}
-
-impl<T> Drop for Queue<T> {
-    fn drop(&mut self) {
-        unsafe {
-            let mut cur = *self.tail.get();
-            while !cur.is_null() {
-                let next = (*cur).next.load(Ordering::Relaxed);
-                let _: Box<Node<T>> = Box::from_raw(cur);
-                cur = next;
-            }
-        }
-    }
-}
diff --git a/library/std/src/sync/mpsc/mpsc_queue/tests.rs b/library/std/src/sync/mpsc/mpsc_queue/tests.rs
deleted file mode 100644
index 34b2a9a98ac..00000000000
--- a/library/std/src/sync/mpsc/mpsc_queue/tests.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use super::{Data, Empty, Inconsistent, Queue};
-use crate::sync::mpsc::channel;
-use crate::sync::Arc;
-use crate::thread;
-
-#[test]
-fn test_full() {
-    let q: Queue<Box<_>> = Queue::new();
-    q.push(Box::new(1));
-    q.push(Box::new(2));
-}
-
-#[test]
-fn test() {
-    let nthreads = 8;
-    let nmsgs = if cfg!(miri) { 100 } else { 1000 };
-    let q = Queue::new();
-    match q.pop() {
-        Empty => {}
-        Inconsistent | Data(..) => panic!(),
-    }
-    let (tx, rx) = channel();
-    let q = Arc::new(q);
-
-    for _ in 0..nthreads {
-        let tx = tx.clone();
-        let q = q.clone();
-        thread::spawn(move || {
-            for i in 0..nmsgs {
-                q.push(i);
-            }
-            tx.send(()).unwrap();
-        });
-    }
-
-    let mut i = 0;
-    while i < nthreads * nmsgs {
-        match q.pop() {
-            Empty | Inconsistent => {}
-            Data(_) => i += 1,
-        }
-    }
-    drop(tx);
-    for _ in 0..nthreads {
-        rx.recv().unwrap();
-    }
-}
diff --git a/library/std/src/sync/mpsc/oneshot.rs b/library/std/src/sync/mpsc/oneshot.rs
deleted file mode 100644
index 0e259b8aecb..00000000000
--- a/library/std/src/sync/mpsc/oneshot.rs
+++ /dev/null
@@ -1,315 +0,0 @@
-/// Oneshot channels/ports
-///
-/// This is the initial flavor of channels/ports used for comm module. This is
-/// an optimization for the one-use case of a channel. The major optimization of
-/// this type is to have one and exactly one allocation when the chan/port pair
-/// is created.
-///
-/// Another possible optimization would be to not use an Arc box because
-/// in theory we know when the shared packet can be deallocated (no real need
-/// for the atomic reference counting), but I was having trouble how to destroy
-/// the data early in a drop of a Port.
-///
-/// # Implementation
-///
-/// Oneshots are implemented around one atomic usize variable. This variable
-/// indicates both the state of the port/chan but also contains any threads
-/// blocked on the port. All atomic operations happen on this one word.
-///
-/// In order to upgrade a oneshot channel, an upgrade is considered a disconnect
-/// on behalf of the channel side of things (it can be mentally thought of as
-/// consuming the port). This upgrade is then also stored in the shared packet.
-/// The one caveat to consider is that when a port sees a disconnected channel
-/// it must check for data because there is no "data plus upgrade" state.
-pub use self::Failure::*;
-use self::MyUpgrade::*;
-pub use self::UpgradeResult::*;
-
-use crate::cell::UnsafeCell;
-use crate::ptr;
-use crate::sync::atomic::{AtomicPtr, Ordering};
-use crate::sync::mpsc::blocking::{self, SignalToken};
-use crate::sync::mpsc::Receiver;
-use crate::time::Instant;
-
-// Various states you can find a port in.
-const EMPTY: *mut u8 = ptr::invalid_mut::<u8>(0); // initial state: no data, no blocked receiver
-const DATA: *mut u8 = ptr::invalid_mut::<u8>(1); // data ready for receiver to take
-const DISCONNECTED: *mut u8 = ptr::invalid_mut::<u8>(2); // channel is disconnected OR upgraded
-// Any other value represents a pointer to a SignalToken value. The
-// protocol ensures that when the state moves *to* a pointer,
-// ownership of the token is given to the packet, and when the state
-// moves *from* a pointer, ownership of the token is transferred to
-// whoever changed the state.
-
-pub struct Packet<T> {
-    // Internal state of the chan/port pair (stores the blocked thread as well)
-    state: AtomicPtr<u8>,
-    // One-shot data slot location
-    data: UnsafeCell<Option<T>>,
-    // when used for the second time, a oneshot channel must be upgraded, and
-    // this contains the slot for the upgrade
-    upgrade: UnsafeCell<MyUpgrade<T>>,
-}
-
-pub enum Failure<T> {
-    Empty,
-    Disconnected,
-    Upgraded(Receiver<T>),
-}
-
-pub enum UpgradeResult {
-    UpSuccess,
-    UpDisconnected,
-    UpWoke(SignalToken),
-}
-
-enum MyUpgrade<T> {
-    NothingSent,
-    SendUsed,
-    GoUp(Receiver<T>),
-}
-
-impl<T> Packet<T> {
-    pub fn new() -> Packet<T> {
-        Packet {
-            data: UnsafeCell::new(None),
-            upgrade: UnsafeCell::new(NothingSent),
-            state: AtomicPtr::new(EMPTY),
-        }
-    }
-
-    pub fn send(&self, t: T) -> Result<(), T> {
-        unsafe {
-            // Sanity check
-            match *self.upgrade.get() {
-                NothingSent => {}
-                _ => panic!("sending on a oneshot that's already sent on "),
-            }
-            assert!((*self.data.get()).is_none());
-            ptr::write(self.data.get(), Some(t));
-            ptr::write(self.upgrade.get(), SendUsed);
-
-            match self.state.swap(DATA, Ordering::SeqCst) {
-                // Sent the data, no one was waiting
-                EMPTY => Ok(()),
-
-                // Couldn't send the data, the port hung up first. Return the data
-                // back up the stack.
-                DISCONNECTED => {
-                    self.state.swap(DISCONNECTED, Ordering::SeqCst);
-                    ptr::write(self.upgrade.get(), NothingSent);
-                    Err((&mut *self.data.get()).take().unwrap())
-                }
-
-                // Not possible, these are one-use channels
-                DATA => unreachable!(),
-
-                // There is a thread waiting on the other end. We leave the 'DATA'
-                // state inside so it'll pick it up on the other end.
-                ptr => {
-                    SignalToken::from_raw(ptr).signal();
-                    Ok(())
-                }
-            }
-        }
-    }
-
-    // Just tests whether this channel has been sent on or not, this is only
-    // safe to use from the sender.
-    pub fn sent(&self) -> bool {
-        unsafe { !matches!(*self.upgrade.get(), NothingSent) }
-    }
-
-    pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure<T>> {
-        // Attempt to not block the thread (it's a little expensive). If it looks
-        // like we're not empty, then immediately go through to `try_recv`.
-        if self.state.load(Ordering::SeqCst) == EMPTY {
-            let (wait_token, signal_token) = blocking::tokens();
-            let ptr = unsafe { signal_token.to_raw() };
-
-            // race with senders to enter the blocking state
-            if self.state.compare_exchange(EMPTY, ptr, Ordering::SeqCst, Ordering::SeqCst).is_ok() {
-                if let Some(deadline) = deadline {
-                    let timed_out = !wait_token.wait_max_until(deadline);
-                    // Try to reset the state
-                    if timed_out {
-                        self.abort_selection().map_err(Upgraded)?;
-                    }
-                } else {
-                    wait_token.wait();
-                    debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY);
-                }
-            } else {
-                // drop the signal token, since we never blocked
-                drop(unsafe { SignalToken::from_raw(ptr) });
-            }
-        }
-
-        self.try_recv()
-    }
-
-    pub fn try_recv(&self) -> Result<T, Failure<T>> {
-        unsafe {
-            match self.state.load(Ordering::SeqCst) {
-                EMPTY => Err(Empty),
-
-                // We saw some data on the channel, but the channel can be used
-                // again to send us an upgrade. As a result, we need to re-insert
-                // into the channel that there's no data available (otherwise we'll
-                // just see DATA next time). This is done as a cmpxchg because if
-                // the state changes under our feet we'd rather just see that state
-                // change.
-                DATA => {
-                    let _ = self.state.compare_exchange(
-                        DATA,
-                        EMPTY,
-                        Ordering::SeqCst,
-                        Ordering::SeqCst,
-                    );
-                    match (&mut *self.data.get()).take() {
-                        Some(data) => Ok(data),
-                        None => unreachable!(),
-                    }
-                }
-
-                // There's no guarantee that we receive before an upgrade happens,
-                // and an upgrade flags the channel as disconnected, so when we see
-                // this we first need to check if there's data available and *then*
-                // we go through and process the upgrade.
-                DISCONNECTED => match (&mut *self.data.get()).take() {
-                    Some(data) => Ok(data),
-                    None => match ptr::replace(self.upgrade.get(), SendUsed) {
-                        SendUsed | NothingSent => Err(Disconnected),
-                        GoUp(upgrade) => Err(Upgraded(upgrade)),
-                    },
-                },
-
-                // We are the sole receiver; there cannot be a blocking
-                // receiver already.
-                _ => unreachable!(),
-            }
-        }
-    }
-
-    // Returns whether the upgrade was completed. If the upgrade wasn't
-    // completed, then the port couldn't get sent to the other half (it will
-    // never receive it).
-    pub fn upgrade(&self, up: Receiver<T>) -> UpgradeResult {
-        unsafe {
-            let prev = match *self.upgrade.get() {
-                NothingSent => NothingSent,
-                SendUsed => SendUsed,
-                _ => panic!("upgrading again"),
-            };
-            ptr::write(self.upgrade.get(), GoUp(up));
-
-            match self.state.swap(DISCONNECTED, Ordering::SeqCst) {
-                // If the channel is empty or has data on it, then we're good to go.
-                // Senders will check the data before the upgrade (in case we
-                // plastered over the DATA state).
-                DATA | EMPTY => UpSuccess,
-
-                // If the other end is already disconnected, then we failed the
-                // upgrade. Be sure to trash the port we were given.
-                DISCONNECTED => {
-                    ptr::replace(self.upgrade.get(), prev);
-                    UpDisconnected
-                }
-
-                // If someone's waiting, we gotta wake them up
-                ptr => UpWoke(SignalToken::from_raw(ptr)),
-            }
-        }
-    }
-
-    pub fn drop_chan(&self) {
-        match self.state.swap(DISCONNECTED, Ordering::SeqCst) {
-            DATA | DISCONNECTED | EMPTY => {}
-
-            // If someone's waiting, we gotta wake them up
-            ptr => unsafe {
-                SignalToken::from_raw(ptr).signal();
-            },
-        }
-    }
-
-    pub fn drop_port(&self) {
-        match self.state.swap(DISCONNECTED, Ordering::SeqCst) {
-            // An empty channel has nothing to do, and a remotely disconnected
-            // channel also has nothing to do b/c we're about to run the drop
-            // glue
-            DISCONNECTED | EMPTY => {}
-
-            // There's data on the channel, so make sure we destroy it promptly.
-            // This is why not using an arc is a little difficult (need the box
-            // to stay valid while we take the data).
-            DATA => unsafe {
-                (&mut *self.data.get()).take().unwrap();
-            },
-
-            // We're the only ones that can block on this port
-            _ => unreachable!(),
-        }
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // select implementation
-    ////////////////////////////////////////////////////////////////////////////
-
-    // Remove a previous selecting thread from this port. This ensures that the
-    // blocked thread will no longer be visible to any other threads.
-    //
-    // The return value indicates whether there's data on this port.
-    pub fn abort_selection(&self) -> Result<bool, Receiver<T>> {
-        let state = match self.state.load(Ordering::SeqCst) {
-            // Each of these states means that no further activity will happen
-            // with regard to abortion selection
-            s @ (EMPTY | DATA | DISCONNECTED) => s,
-
-            // If we've got a blocked thread, then use an atomic to gain ownership
-            // of it (may fail)
-            ptr => self
-                .state
-                .compare_exchange(ptr, EMPTY, Ordering::SeqCst, Ordering::SeqCst)
-                .unwrap_or_else(|x| x),
-        };
-
-        // Now that we've got ownership of our state, figure out what to do
-        // about it.
-        match state {
-            EMPTY => unreachable!(),
-            // our thread used for select was stolen
-            DATA => Ok(true),
-
-            // If the other end has hung up, then we have complete ownership
-            // of the port. First, check if there was data waiting for us. This
-            // is possible if the other end sent something and then hung up.
-            //
-            // We then need to check to see if there was an upgrade requested,
-            // and if so, the upgraded port needs to have its selection aborted.
-            DISCONNECTED => unsafe {
-                if (*self.data.get()).is_some() {
-                    Ok(true)
-                } else {
-                    match ptr::replace(self.upgrade.get(), SendUsed) {
-                        GoUp(port) => Err(port),
-                        _ => Ok(true),
-                    }
-                }
-            },
-
-            // We woke ourselves up from select.
-            ptr => unsafe {
-                drop(SignalToken::from_raw(ptr));
-                Ok(false)
-            },
-        }
-    }
-}
-
-impl<T> Drop for Packet<T> {
-    fn drop(&mut self) {
-        assert_eq!(self.state.load(Ordering::SeqCst), DISCONNECTED);
-    }
-}
diff --git a/library/std/src/sync/mpsc/shared.rs b/library/std/src/sync/mpsc/shared.rs
deleted file mode 100644
index 51917bd96bd..00000000000
--- a/library/std/src/sync/mpsc/shared.rs
+++ /dev/null
@@ -1,501 +0,0 @@
-/// Shared channels.
-///
-/// This is the flavor of channels which are not necessarily optimized for any
-/// particular use case, but are the most general in how they are used. Shared
-/// channels are cloneable allowing for multiple senders.
-///
-/// High level implementation details can be found in the comment of the parent
-/// module. You'll also note that the implementation of the shared and stream
-/// channels are quite similar, and this is no coincidence!
-pub use self::Failure::*;
-use self::StartResult::*;
-
-use core::cmp;
-use core::intrinsics::abort;
-
-use crate::cell::UnsafeCell;
-use crate::ptr;
-use crate::sync::atomic::{AtomicBool, AtomicIsize, AtomicPtr, AtomicUsize, Ordering};
-use crate::sync::mpsc::blocking::{self, SignalToken};
-use crate::sync::mpsc::mpsc_queue as mpsc;
-use crate::sync::{Mutex, MutexGuard};
-use crate::thread;
-use crate::time::Instant;
-
-const DISCONNECTED: isize = isize::MIN;
-const FUDGE: isize = 1024;
-const MAX_REFCOUNT: usize = (isize::MAX) as usize;
-#[cfg(test)]
-const MAX_STEALS: isize = 5;
-#[cfg(not(test))]
-const MAX_STEALS: isize = 1 << 20;
-const EMPTY: *mut u8 = ptr::null_mut(); // initial state: no data, no blocked receiver
-
-pub struct Packet<T> {
-    queue: mpsc::Queue<T>,
-    cnt: AtomicIsize,          // How many items are on this channel
-    steals: UnsafeCell<isize>, // How many times has a port received without blocking?
-    to_wake: AtomicPtr<u8>,    // SignalToken for wake up
-
-    // The number of channels which are currently using this packet.
-    channels: AtomicUsize,
-
-    // See the discussion in Port::drop and the channel send methods for what
-    // these are used for
-    port_dropped: AtomicBool,
-    sender_drain: AtomicIsize,
-
-    // this lock protects various portions of this implementation during
-    // select()
-    select_lock: Mutex<()>,
-}
-
-pub enum Failure {
-    Empty,
-    Disconnected,
-}
-
-#[derive(PartialEq, Eq)]
-enum StartResult {
-    Installed,
-    Abort,
-}
-
-impl<T> Packet<T> {
-    // Creation of a packet *must* be followed by a call to postinit_lock
-    // and later by inherit_blocker
-    pub fn new() -> Packet<T> {
-        Packet {
-            queue: mpsc::Queue::new(),
-            cnt: AtomicIsize::new(0),
-            steals: UnsafeCell::new(0),
-            to_wake: AtomicPtr::new(EMPTY),
-            channels: AtomicUsize::new(2),
-            port_dropped: AtomicBool::new(false),
-            sender_drain: AtomicIsize::new(0),
-            select_lock: Mutex::new(()),
-        }
-    }
-
-    // This function should be used after newly created Packet
-    // was wrapped with an Arc
-    // In other case mutex data will be duplicated while cloning
-    // and that could cause problems on platforms where it is
-    // represented by opaque data structure
-    pub fn postinit_lock(&self) -> MutexGuard<'_, ()> {
-        self.select_lock.lock().unwrap()
-    }
-
-    // This function is used at the creation of a shared packet to inherit a
-    // previously blocked thread. This is done to prevent spurious wakeups of
-    // threads in select().
-    //
-    // This can only be called at channel-creation time
-    pub fn inherit_blocker(&self, token: Option<SignalToken>, guard: MutexGuard<'_, ()>) {
-        if let Some(token) = token {
-            assert_eq!(self.cnt.load(Ordering::SeqCst), 0);
-            assert_eq!(self.to_wake.load(Ordering::SeqCst), EMPTY);
-            self.to_wake.store(unsafe { token.to_raw() }, Ordering::SeqCst);
-            self.cnt.store(-1, Ordering::SeqCst);
-
-            // This store is a little sketchy. What's happening here is that
-            // we're transferring a blocker from a oneshot or stream channel to
-            // this shared channel. In doing so, we never spuriously wake them
-            // up and rather only wake them up at the appropriate time. This
-            // implementation of shared channels assumes that any blocking
-            // recv() will undo the increment of steals performed in try_recv()
-            // once the recv is complete.  This thread that we're inheriting,
-            // however, is not in the middle of recv. Hence, the first time we
-            // wake them up, they're going to wake up from their old port, move
-            // on to the upgraded port, and then call the block recv() function.
-            //
-            // When calling this function, they'll find there's data immediately
-            // available, counting it as a steal. This in fact wasn't a steal
-            // because we appropriately blocked them waiting for data.
-            //
-            // To offset this bad increment, we initially set the steal count to
-            // -1. You'll find some special code in abort_selection() as well to
-            // ensure that this -1 steal count doesn't escape too far.
-            unsafe {
-                *self.steals.get() = -1;
-            }
-        }
-
-        // When the shared packet is constructed, we grabbed this lock. The
-        // purpose of this lock is to ensure that abort_selection() doesn't
-        // interfere with this method. After we unlock this lock, we're
-        // signifying that we're done modifying self.cnt and self.to_wake and
-        // the port is ready for the world to continue using it.
-        drop(guard);
-    }
-
-    pub fn send(&self, t: T) -> Result<(), T> {
-        // See Port::drop for what's going on
-        if self.port_dropped.load(Ordering::SeqCst) {
-            return Err(t);
-        }
-
-        // Note that the multiple sender case is a little trickier
-        // semantically than the single sender case. The logic for
-        // incrementing is "add and if disconnected store disconnected".
-        // This could end up leading some senders to believe that there
-        // wasn't a disconnect if in fact there was a disconnect. This means
-        // that while one thread is attempting to re-store the disconnected
-        // states, other threads could walk through merrily incrementing
-        // this very-negative disconnected count. To prevent senders from
-        // spuriously attempting to send when the channels is actually
-        // disconnected, the count has a ranged check here.
-        //
-        // This is also done for another reason. Remember that the return
-        // value of this function is:
-        //
-        //  `true` == the data *may* be received, this essentially has no
-        //            meaning
-        //  `false` == the data will *never* be received, this has a lot of
-        //             meaning
-        //
-        // In the SPSC case, we have a check of 'queue.is_empty()' to see
-        // whether the data was actually received, but this same condition
-        // means nothing in a multi-producer context. As a result, this
-        // preflight check serves as the definitive "this will never be
-        // received". Once we get beyond this check, we have permanently
-        // entered the realm of "this may be received"
-        if self.cnt.load(Ordering::SeqCst) < DISCONNECTED + FUDGE {
-            return Err(t);
-        }
-
-        self.queue.push(t);
-        match self.cnt.fetch_add(1, Ordering::SeqCst) {
-            -1 => {
-                self.take_to_wake().signal();
-            }
-
-            // In this case, we have possibly failed to send our data, and
-            // we need to consider re-popping the data in order to fully
-            // destroy it. We must arbitrate among the multiple senders,
-            // however, because the queues that we're using are
-            // single-consumer queues. In order to do this, all exiting
-            // pushers will use an atomic count in order to count those
-            // flowing through. Pushers who see 0 are required to drain as
-            // much as possible, and then can only exit when they are the
-            // only pusher (otherwise they must try again).
-            n if n < DISCONNECTED + FUDGE => {
-                // see the comment in 'try' for a shared channel for why this
-                // window of "not disconnected" is ok.
-                self.cnt.store(DISCONNECTED, Ordering::SeqCst);
-
-                if self.sender_drain.fetch_add(1, Ordering::SeqCst) == 0 {
-                    loop {
-                        // drain the queue, for info on the thread yield see the
-                        // discussion in try_recv
-                        loop {
-                            match self.queue.pop() {
-                                mpsc::Data(..) => {}
-                                mpsc::Empty => break,
-                                mpsc::Inconsistent => thread::yield_now(),
-                            }
-                        }
-                        // maybe we're done, if we're not the last ones
-                        // here, then we need to go try again.
-                        if self.sender_drain.fetch_sub(1, Ordering::SeqCst) == 1 {
-                            break;
-                        }
-                    }
-
-                    // At this point, there may still be data on the queue,
-                    // but only if the count hasn't been incremented and
-                    // some other sender hasn't finished pushing data just
-                    // yet. That sender in question will drain its own data.
-                }
-            }
-
-            // Can't make any assumptions about this case like in the SPSC case.
-            _ => {}
-        }
-
-        Ok(())
-    }
-
-    pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure> {
-        // This code is essentially the exact same as that found in the stream
-        // case (see stream.rs)
-        match self.try_recv() {
-            Err(Empty) => {}
-            data => return data,
-        }
-
-        let (wait_token, signal_token) = blocking::tokens();
-        if self.decrement(signal_token) == Installed {
-            if let Some(deadline) = deadline {
-                let timed_out = !wait_token.wait_max_until(deadline);
-                if timed_out {
-                    self.abort_selection(false);
-                }
-            } else {
-                wait_token.wait();
-            }
-        }
-
-        match self.try_recv() {
-            data @ Ok(..) => unsafe {
-                *self.steals.get() -= 1;
-                data
-            },
-            data => data,
-        }
-    }
-
-    // Essentially the exact same thing as the stream decrement function.
-    // Returns true if blocking should proceed.
-    fn decrement(&self, token: SignalToken) -> StartResult {
-        unsafe {
-            assert_eq!(
-                self.to_wake.load(Ordering::SeqCst),
-                EMPTY,
-                "This is a known bug in the Rust standard library. See https://github.com/rust-lang/rust/issues/39364"
-            );
-            let ptr = token.to_raw();
-            self.to_wake.store(ptr, Ordering::SeqCst);
-
-            let steals = ptr::replace(self.steals.get(), 0);
-
-            match self.cnt.fetch_sub(1 + steals, Ordering::SeqCst) {
-                DISCONNECTED => {
-                    self.cnt.store(DISCONNECTED, Ordering::SeqCst);
-                }
-                // If we factor in our steals and notice that the channel has no
-                // data, we successfully sleep
-                n => {
-                    assert!(n >= 0);
-                    if n - steals <= 0 {
-                        return Installed;
-                    }
-                }
-            }
-
-            self.to_wake.store(EMPTY, Ordering::SeqCst);
-            drop(SignalToken::from_raw(ptr));
-            Abort
-        }
-    }
-
-    pub fn try_recv(&self) -> Result<T, Failure> {
-        let ret = match self.queue.pop() {
-            mpsc::Data(t) => Some(t),
-            mpsc::Empty => None,
-
-            // This is a bit of an interesting case. The channel is reported as
-            // having data available, but our pop() has failed due to the queue
-            // being in an inconsistent state.  This means that there is some
-            // pusher somewhere which has yet to complete, but we are guaranteed
-            // that a pop will eventually succeed. In this case, we spin in a
-            // yield loop because the remote sender should finish their enqueue
-            // operation "very quickly".
-            //
-            // Avoiding this yield loop would require a different queue
-            // abstraction which provides the guarantee that after M pushes have
-            // succeeded, at least M pops will succeed. The current queues
-            // guarantee that if there are N active pushes, you can pop N times
-            // once all N have finished.
-            mpsc::Inconsistent => {
-                let data;
-                loop {
-                    thread::yield_now();
-                    match self.queue.pop() {
-                        mpsc::Data(t) => {
-                            data = t;
-                            break;
-                        }
-                        mpsc::Empty => panic!("inconsistent => empty"),
-                        mpsc::Inconsistent => {}
-                    }
-                }
-                Some(data)
-            }
-        };
-        match ret {
-            // See the discussion in the stream implementation for why we
-            // might decrement steals.
-            Some(data) => unsafe {
-                if *self.steals.get() > MAX_STEALS {
-                    match self.cnt.swap(0, Ordering::SeqCst) {
-                        DISCONNECTED => {
-                            self.cnt.store(DISCONNECTED, Ordering::SeqCst);
-                        }
-                        n => {
-                            let m = cmp::min(n, *self.steals.get());
-                            *self.steals.get() -= m;
-                            self.bump(n - m);
-                        }
-                    }
-                    assert!(*self.steals.get() >= 0);
-                }
-                *self.steals.get() += 1;
-                Ok(data)
-            },
-
-            // See the discussion in the stream implementation for why we try
-            // again.
-            None => {
-                match self.cnt.load(Ordering::SeqCst) {
-                    n if n != DISCONNECTED => Err(Empty),
-                    _ => {
-                        match self.queue.pop() {
-                            mpsc::Data(t) => Ok(t),
-                            mpsc::Empty => Err(Disconnected),
-                            // with no senders, an inconsistency is impossible.
-                            mpsc::Inconsistent => unreachable!(),
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    // Prepares this shared packet for a channel clone, essentially just bumping
-    // a refcount.
-    pub fn clone_chan(&self) {
-        let old_count = self.channels.fetch_add(1, Ordering::SeqCst);
-
-        // See comments on Arc::clone() on why we do this (for `mem::forget`).
-        if old_count > MAX_REFCOUNT {
-            abort();
-        }
-    }
-
-    // Decrement the reference count on a channel. This is called whenever a
-    // Chan is dropped and may end up waking up a receiver. It's the receiver's
-    // responsibility on the other end to figure out that we've disconnected.
-    pub fn drop_chan(&self) {
-        match self.channels.fetch_sub(1, Ordering::SeqCst) {
-            1 => {}
-            n if n > 1 => return,
-            n => panic!("bad number of channels left {n}"),
-        }
-
-        match self.cnt.swap(DISCONNECTED, Ordering::SeqCst) {
-            -1 => {
-                self.take_to_wake().signal();
-            }
-            DISCONNECTED => {}
-            n => {
-                assert!(n >= 0);
-            }
-        }
-    }
-
-    // See the long discussion inside of stream.rs for why the queue is drained,
-    // and why it is done in this fashion.
-    pub fn drop_port(&self) {
-        self.port_dropped.store(true, Ordering::SeqCst);
-        let mut steals = unsafe { *self.steals.get() };
-        while {
-            match self.cnt.compare_exchange(
-                steals,
-                DISCONNECTED,
-                Ordering::SeqCst,
-                Ordering::SeqCst,
-            ) {
-                Ok(_) => false,
-                Err(old) => old != DISCONNECTED,
-            }
-        } {
-            // See the discussion in 'try_recv' for why we yield
-            // control of this thread.
-            loop {
-                match self.queue.pop() {
-                    mpsc::Data(..) => {
-                        steals += 1;
-                    }
-                    mpsc::Empty | mpsc::Inconsistent => break,
-                }
-            }
-        }
-    }
-
-    // Consumes ownership of the 'to_wake' field.
-    fn take_to_wake(&self) -> SignalToken {
-        let ptr = self.to_wake.load(Ordering::SeqCst);
-        self.to_wake.store(EMPTY, Ordering::SeqCst);
-        assert!(ptr != EMPTY);
-        unsafe { SignalToken::from_raw(ptr) }
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // select implementation
-    ////////////////////////////////////////////////////////////////////////////
-
-    // increment the count on the channel (used for selection)
-    fn bump(&self, amt: isize) -> isize {
-        match self.cnt.fetch_add(amt, Ordering::SeqCst) {
-            DISCONNECTED => {
-                self.cnt.store(DISCONNECTED, Ordering::SeqCst);
-                DISCONNECTED
-            }
-            n => n,
-        }
-    }
-
-    // Cancels a previous thread waiting on this port, returning whether there's
-    // data on the port.
-    //
-    // This is similar to the stream implementation (hence fewer comments), but
-    // uses a different value for the "steals" variable.
-    pub fn abort_selection(&self, _was_upgrade: bool) -> bool {
-        // Before we do anything else, we bounce on this lock. The reason for
-        // doing this is to ensure that any upgrade-in-progress is gone and
-        // done with. Without this bounce, we can race with inherit_blocker
-        // about looking at and dealing with to_wake. Once we have acquired the
-        // lock, we are guaranteed that inherit_blocker is done.
-        {
-            let _guard = self.select_lock.lock().unwrap();
-        }
-
-        // Like the stream implementation, we want to make sure that the count
-        // on the channel goes non-negative. We don't know how negative the
-        // stream currently is, so instead of using a steal value of 1, we load
-        // the channel count and figure out what we should do to make it
-        // positive.
-        let steals = {
-            let cnt = self.cnt.load(Ordering::SeqCst);
-            if cnt < 0 && cnt != DISCONNECTED { -cnt } else { 0 }
-        };
-        let prev = self.bump(steals + 1);
-
-        if prev == DISCONNECTED {
-            assert_eq!(self.to_wake.load(Ordering::SeqCst), EMPTY);
-            true
-        } else {
-            let cur = prev + steals + 1;
-            assert!(cur >= 0);
-            if prev < 0 {
-                drop(self.take_to_wake());
-            } else {
-                while self.to_wake.load(Ordering::SeqCst) != EMPTY {
-                    thread::yield_now();
-                }
-            }
-            unsafe {
-                // if the number of steals is -1, it was the pre-emptive -1 steal
-                // count from when we inherited a blocker. This is fine because
-                // we're just going to overwrite it with a real value.
-                let old = self.steals.get();
-                assert!(*old == 0 || *old == -1);
-                *old = steals;
-                prev >= 0
-            }
-        }
-    }
-}
-
-impl<T> Drop for Packet<T> {
-    fn drop(&mut self) {
-        // Note that this load is not only an assert for correctness about
-        // disconnection, but also a proper fence before the read of
-        // `to_wake`, so this assert cannot be removed with also removing
-        // the `to_wake` assert.
-        assert_eq!(self.cnt.load(Ordering::SeqCst), DISCONNECTED);
-        assert_eq!(self.to_wake.load(Ordering::SeqCst), EMPTY);
-        assert_eq!(self.channels.load(Ordering::SeqCst), 0);
-    }
-}
diff --git a/library/std/src/sync/mpsc/spsc_queue.rs b/library/std/src/sync/mpsc/spsc_queue.rs
deleted file mode 100644
index 61f91313ea9..00000000000
--- a/library/std/src/sync/mpsc/spsc_queue.rs
+++ /dev/null
@@ -1,244 +0,0 @@
-//! A single-producer single-consumer concurrent queue
-//!
-//! This module contains the implementation of an SPSC queue which can be used
-//! concurrently between two threads. This data structure is safe to use and
-//! enforces the semantics that there is one pusher and one popper.
-
-// The original implementation is based off:
-// https://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue
-//
-// Note that back when the code was imported, it was licensed under the BSD-2-Clause license:
-// http://web.archive.org/web/20110411011612/https://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue
-//
-// The original author of the code agreed to relicense it under `MIT OR Apache-2.0` in 2017, so as
-// of today the license of this file is the same as the rest of the codebase:
-// https://github.com/rust-lang/rust/pull/42149
-
-#[cfg(all(test, not(target_os = "emscripten")))]
-mod tests;
-
-use core::cell::UnsafeCell;
-use core::ptr;
-
-use crate::boxed::Box;
-use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
-
-use super::cache_aligned::CacheAligned;
-
-// Node within the linked list queue of messages to send
-struct Node<T> {
-    // FIXME: this could be an uninitialized T if we're careful enough, and
-    //      that would reduce memory usage (and be a bit faster).
-    //      is it worth it?
-    value: Option<T>,         // nullable for re-use of nodes
-    cached: bool,             // This node goes into the node cache
-    next: AtomicPtr<Node<T>>, // next node in the queue
-}
-
-/// The single-producer single-consumer queue. This structure is not cloneable,
-/// but it can be safely shared in an Arc if it is guaranteed that there
-/// is only one popper and one pusher touching the queue at any one point in
-/// time.
-pub struct Queue<T, ProducerAddition = (), ConsumerAddition = ()> {
-    // consumer fields
-    consumer: CacheAligned<Consumer<T, ConsumerAddition>>,
-
-    // producer fields
-    producer: CacheAligned<Producer<T, ProducerAddition>>,
-}
-
-struct Consumer<T, Addition> {
-    tail: UnsafeCell<*mut Node<T>>, // where to pop from
-    tail_prev: AtomicPtr<Node<T>>,  // where to pop from
-    cache_bound: usize,             // maximum cache size
-    cached_nodes: AtomicUsize,      // number of nodes marked as cacheable
-    addition: Addition,
-}
-
-struct Producer<T, Addition> {
-    head: UnsafeCell<*mut Node<T>>,      // where to push to
-    first: UnsafeCell<*mut Node<T>>,     // where to get new nodes from
-    tail_copy: UnsafeCell<*mut Node<T>>, // between first/tail
-    addition: Addition,
-}
-
-unsafe impl<T: Send, P: Send + Sync, C: Send + Sync> Send for Queue<T, P, C> {}
-
-unsafe impl<T: Send, P: Send + Sync, C: Send + Sync> Sync for Queue<T, P, C> {}
-
-impl<T> Node<T> {
-    fn new() -> *mut Node<T> {
-        Box::into_raw(box Node {
-            value: None,
-            cached: false,
-            next: AtomicPtr::new(ptr::null_mut::<Node<T>>()),
-        })
-    }
-}
-
-impl<T, ProducerAddition, ConsumerAddition> Queue<T, ProducerAddition, ConsumerAddition> {
-    /// Creates a new queue. With given additional elements in the producer and
-    /// consumer portions of the queue.
-    ///
-    /// Due to the performance implications of cache-contention,
-    /// we wish to keep fields used mainly by the producer on a separate cache
-    /// line than those used by the consumer.
-    /// Since cache lines are usually 64 bytes, it is unreasonably expensive to
-    /// allocate one for small fields, so we allow users to insert additional
-    /// fields into the cache lines already allocated by this for the producer
-    /// and consumer.
-    ///
-    /// This is unsafe as the type system doesn't enforce a single
-    /// consumer-producer relationship. It also allows the consumer to `pop`
-    /// items while there is a `peek` active due to all methods having a
-    /// non-mutable receiver.
-    ///
-    /// # Arguments
-    ///
-    ///   * `bound` - This queue implementation is implemented with a linked
-    ///               list, and this means that a push is always a malloc. In
-    ///               order to amortize this cost, an internal cache of nodes is
-    ///               maintained to prevent a malloc from always being
-    ///               necessary. This bound is the limit on the size of the
-    ///               cache (if desired). If the value is 0, then the cache has
-    ///               no bound. Otherwise, the cache will never grow larger than
-    ///               `bound` (although the queue itself could be much larger.
-    pub unsafe fn with_additions(
-        bound: usize,
-        producer_addition: ProducerAddition,
-        consumer_addition: ConsumerAddition,
-    ) -> Self {
-        let n1 = Node::new();
-        let n2 = Node::new();
-        (*n1).next.store(n2, Ordering::Relaxed);
-        Queue {
-            consumer: CacheAligned::new(Consumer {
-                tail: UnsafeCell::new(n2),
-                tail_prev: AtomicPtr::new(n1),
-                cache_bound: bound,
-                cached_nodes: AtomicUsize::new(0),
-                addition: consumer_addition,
-            }),
-            producer: CacheAligned::new(Producer {
-                head: UnsafeCell::new(n2),
-                first: UnsafeCell::new(n1),
-                tail_copy: UnsafeCell::new(n1),
-                addition: producer_addition,
-            }),
-        }
-    }
-
-    /// Pushes a new value onto this queue. Note that to use this function
-    /// safely, it must be externally guaranteed that there is only one pusher.
-    pub fn push(&self, t: T) {
-        unsafe {
-            // Acquire a node (which either uses a cached one or allocates a new
-            // one), and then append this to the 'head' node.
-            let n = self.alloc();
-            assert!((*n).value.is_none());
-            (*n).value = Some(t);
-            (*n).next.store(ptr::null_mut(), Ordering::Relaxed);
-            (**self.producer.head.get()).next.store(n, Ordering::Release);
-            *(&self.producer.head).get() = n;
-        }
-    }
-
-    unsafe fn alloc(&self) -> *mut Node<T> {
-        // First try to see if we can consume the 'first' node for our uses.
-        if *self.producer.first.get() != *self.producer.tail_copy.get() {
-            let ret = *self.producer.first.get();
-            *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed);
-            return ret;
-        }
-        // If the above fails, then update our copy of the tail and try
-        // again.
-        *self.producer.0.tail_copy.get() = self.consumer.tail_prev.load(Ordering::Acquire);
-        if *self.producer.first.get() != *self.producer.tail_copy.get() {
-            let ret = *self.producer.first.get();
-            *self.producer.0.first.get() = (*ret).next.load(Ordering::Relaxed);
-            return ret;
-        }
-        // If all of that fails, then we have to allocate a new node
-        // (there's nothing in the node cache).
-        Node::new()
-    }
-
-    /// Attempts to pop a value from this queue. Remember that to use this type
-    /// safely you must ensure that there is only one popper at a time.
-    pub fn pop(&self) -> Option<T> {
-        unsafe {
-            // The `tail` node is not actually a used node, but rather a
-            // sentinel from where we should start popping from. Hence, look at
-            // tail's next field and see if we can use it. If we do a pop, then
-            // the current tail node is a candidate for going into the cache.
-            let tail = *self.consumer.tail.get();
-            let next = (*tail).next.load(Ordering::Acquire);
-            if next.is_null() {
-                return None;
-            }
-            assert!((*next).value.is_some());
-            let ret = (*next).value.take();
-
-            *self.consumer.0.tail.get() = next;
-            if self.consumer.cache_bound == 0 {
-                self.consumer.tail_prev.store(tail, Ordering::Release);
-            } else {
-                let cached_nodes = self.consumer.cached_nodes.load(Ordering::Relaxed);
-                if cached_nodes < self.consumer.cache_bound && !(*tail).cached {
-                    self.consumer.cached_nodes.store(cached_nodes, Ordering::Relaxed);
-                    (*tail).cached = true;
-                }
-
-                if (*tail).cached {
-                    self.consumer.tail_prev.store(tail, Ordering::Release);
-                } else {
-                    (*self.consumer.tail_prev.load(Ordering::Relaxed))
-                        .next
-                        .store(next, Ordering::Relaxed);
-                    // We have successfully erased all references to 'tail', so
-                    // now we can safely drop it.
-                    let _: Box<Node<T>> = Box::from_raw(tail);
-                }
-            }
-            ret
-        }
-    }
-
-    /// Attempts to peek at the head of the queue, returning `None` if the queue
-    /// has no data currently
-    ///
-    /// # Warning
-    /// The reference returned is invalid if it is not used before the consumer
-    /// pops the value off the queue. If the producer then pushes another value
-    /// onto the queue, it will overwrite the value pointed to by the reference.
-    pub fn peek(&self) -> Option<&mut T> {
-        // This is essentially the same as above with all the popping bits
-        // stripped out.
-        unsafe {
-            let tail = *self.consumer.tail.get();
-            let next = (*tail).next.load(Ordering::Acquire);
-            if next.is_null() { None } else { (*next).value.as_mut() }
-        }
-    }
-
-    pub fn producer_addition(&self) -> &ProducerAddition {
-        &self.producer.addition
-    }
-
-    pub fn consumer_addition(&self) -> &ConsumerAddition {
-        &self.consumer.addition
-    }
-}
-
-impl<T, ProducerAddition, ConsumerAddition> Drop for Queue<T, ProducerAddition, ConsumerAddition> {
-    fn drop(&mut self) {
-        unsafe {
-            let mut cur = *self.producer.first.get();
-            while !cur.is_null() {
-                let next = (*cur).next.load(Ordering::Relaxed);
-                let _n: Box<Node<T>> = Box::from_raw(cur);
-                cur = next;
-            }
-        }
-    }
-}
diff --git a/library/std/src/sync/mpsc/spsc_queue/tests.rs b/library/std/src/sync/mpsc/spsc_queue/tests.rs
deleted file mode 100644
index eb6d5c2cf66..00000000000
--- a/library/std/src/sync/mpsc/spsc_queue/tests.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use super::Queue;
-use crate::sync::mpsc::channel;
-use crate::sync::Arc;
-use crate::thread;
-
-#[test]
-fn smoke() {
-    unsafe {
-        let queue = Queue::with_additions(0, (), ());
-        queue.push(1);
-        queue.push(2);
-        assert_eq!(queue.pop(), Some(1));
-        assert_eq!(queue.pop(), Some(2));
-        assert_eq!(queue.pop(), None);
-        queue.push(3);
-        queue.push(4);
-        assert_eq!(queue.pop(), Some(3));
-        assert_eq!(queue.pop(), Some(4));
-        assert_eq!(queue.pop(), None);
-    }
-}
-
-#[test]
-fn peek() {
-    unsafe {
-        let queue = Queue::with_additions(0, (), ());
-        queue.push(vec![1]);
-
-        // Ensure the borrowchecker works
-        match queue.peek() {
-            Some(vec) => {
-                assert_eq!(&*vec, &[1]);
-            }
-            None => unreachable!(),
-        }
-
-        match queue.pop() {
-            Some(vec) => {
-                assert_eq!(&*vec, &[1]);
-            }
-            None => unreachable!(),
-        }
-    }
-}
-
-#[test]
-fn drop_full() {
-    unsafe {
-        let q: Queue<Box<_>> = Queue::with_additions(0, (), ());
-        q.push(Box::new(1));
-        q.push(Box::new(2));
-    }
-}
-
-#[test]
-fn smoke_bound() {
-    unsafe {
-        let q = Queue::with_additions(0, (), ());
-        q.push(1);
-        q.push(2);
-        assert_eq!(q.pop(), Some(1));
-        assert_eq!(q.pop(), Some(2));
-        assert_eq!(q.pop(), None);
-        q.push(3);
-        q.push(4);
-        assert_eq!(q.pop(), Some(3));
-        assert_eq!(q.pop(), Some(4));
-        assert_eq!(q.pop(), None);
-    }
-}
-
-#[test]
-fn stress() {
-    unsafe {
-        stress_bound(0);
-        stress_bound(1);
-    }
-
-    unsafe fn stress_bound(bound: usize) {
-        let count = if cfg!(miri) { 1000 } else { 100000 };
-        let q = Arc::new(Queue::with_additions(bound, (), ()));
-
-        let (tx, rx) = channel();
-        let q2 = q.clone();
-        let _t = thread::spawn(move || {
-            for _ in 0..count {
-                loop {
-                    match q2.pop() {
-                        Some(1) => break,
-                        Some(_) => panic!(),
-                        None => {}
-                    }
-                }
-            }
-            tx.send(()).unwrap();
-        });
-        for _ in 0..count {
-            q.push(1);
-        }
-        rx.recv().unwrap();
-    }
-}
diff --git a/library/std/src/sync/mpsc/stream.rs b/library/std/src/sync/mpsc/stream.rs
deleted file mode 100644
index 4592e914160..00000000000
--- a/library/std/src/sync/mpsc/stream.rs
+++ /dev/null
@@ -1,457 +0,0 @@
-/// Stream channels
-///
-/// This is the flavor of channels which are optimized for one sender and one
-/// receiver. The sender will be upgraded to a shared channel if the channel is
-/// cloned.
-///
-/// High level implementation details can be found in the comment of the parent
-/// module.
-pub use self::Failure::*;
-use self::Message::*;
-pub use self::UpgradeResult::*;
-
-use core::cmp;
-
-use crate::cell::UnsafeCell;
-use crate::ptr;
-use crate::thread;
-use crate::time::Instant;
-
-use crate::sync::atomic::{AtomicBool, AtomicIsize, AtomicPtr, Ordering};
-use crate::sync::mpsc::blocking::{self, SignalToken};
-use crate::sync::mpsc::spsc_queue as spsc;
-use crate::sync::mpsc::Receiver;
-
-const DISCONNECTED: isize = isize::MIN;
-#[cfg(test)]
-const MAX_STEALS: isize = 5;
-#[cfg(not(test))]
-const MAX_STEALS: isize = 1 << 20;
-const EMPTY: *mut u8 = ptr::null_mut(); // initial state: no data, no blocked receiver
-
-pub struct Packet<T> {
-    // internal queue for all messages
-    queue: spsc::Queue<Message<T>, ProducerAddition, ConsumerAddition>,
-}
-
-struct ProducerAddition {
-    cnt: AtomicIsize,       // How many items are on this channel
-    to_wake: AtomicPtr<u8>, // SignalToken for the blocked thread to wake up
-
-    port_dropped: AtomicBool, // flag if the channel has been destroyed.
-}
-
-struct ConsumerAddition {
-    steals: UnsafeCell<isize>, // How many times has a port received without blocking?
-}
-
-pub enum Failure<T> {
-    Empty,
-    Disconnected,
-    Upgraded(Receiver<T>),
-}
-
-pub enum UpgradeResult {
-    UpSuccess,
-    UpDisconnected,
-    UpWoke(SignalToken),
-}
-
-// Any message could contain an "upgrade request" to a new shared port, so the
-// internal queue it's a queue of T, but rather Message<T>
-enum Message<T> {
-    Data(T),
-    GoUp(Receiver<T>),
-}
-
-impl<T> Packet<T> {
-    pub fn new() -> Packet<T> {
-        Packet {
-            queue: unsafe {
-                spsc::Queue::with_additions(
-                    128,
-                    ProducerAddition {
-                        cnt: AtomicIsize::new(0),
-                        to_wake: AtomicPtr::new(EMPTY),
-
-                        port_dropped: AtomicBool::new(false),
-                    },
-                    ConsumerAddition { steals: UnsafeCell::new(0) },
-                )
-            },
-        }
-    }
-
-    pub fn send(&self, t: T) -> Result<(), T> {
-        // If the other port has deterministically gone away, then definitely
-        // must return the data back up the stack. Otherwise, the data is
-        // considered as being sent.
-        if self.queue.producer_addition().port_dropped.load(Ordering::SeqCst) {
-            return Err(t);
-        }
-
-        match self.do_send(Data(t)) {
-            UpSuccess | UpDisconnected => {}
-            UpWoke(token) => {
-                token.signal();
-            }
-        }
-        Ok(())
-    }
-
-    pub fn upgrade(&self, up: Receiver<T>) -> UpgradeResult {
-        // If the port has gone away, then there's no need to proceed any
-        // further.
-        if self.queue.producer_addition().port_dropped.load(Ordering::SeqCst) {
-            return UpDisconnected;
-        }
-
-        self.do_send(GoUp(up))
-    }
-
-    fn do_send(&self, t: Message<T>) -> UpgradeResult {
-        self.queue.push(t);
-        match self.queue.producer_addition().cnt.fetch_add(1, Ordering::SeqCst) {
-            // As described in the mod's doc comment, -1 == wakeup
-            -1 => UpWoke(self.take_to_wake()),
-            // As described before, SPSC queues must be >= -2
-            -2 => UpSuccess,
-
-            // Be sure to preserve the disconnected state, and the return value
-            // in this case is going to be whether our data was received or not.
-            // This manifests itself on whether we have an empty queue or not.
-            //
-            // Primarily, are required to drain the queue here because the port
-            // will never remove this data. We can only have at most one item to
-            // drain (the port drains the rest).
-            DISCONNECTED => {
-                self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst);
-                let first = self.queue.pop();
-                let second = self.queue.pop();
-                assert!(second.is_none());
-
-                match first {
-                    Some(..) => UpSuccess,  // we failed to send the data
-                    None => UpDisconnected, // we successfully sent data
-                }
-            }
-
-            // Otherwise we just sent some data on a non-waiting queue, so just
-            // make sure the world is sane and carry on!
-            n => {
-                assert!(n >= 0);
-                UpSuccess
-            }
-        }
-    }
-
-    // Consumes ownership of the 'to_wake' field.
-    fn take_to_wake(&self) -> SignalToken {
-        let ptr = self.queue.producer_addition().to_wake.load(Ordering::SeqCst);
-        self.queue.producer_addition().to_wake.store(EMPTY, Ordering::SeqCst);
-        assert!(ptr != EMPTY);
-        unsafe { SignalToken::from_raw(ptr) }
-    }
-
-    // Decrements the count on the channel for a sleeper, returning the sleeper
-    // back if it shouldn't sleep. Note that this is the location where we take
-    // steals into account.
-    fn decrement(&self, token: SignalToken) -> Result<(), SignalToken> {
-        assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY);
-        let ptr = unsafe { token.to_raw() };
-        self.queue.producer_addition().to_wake.store(ptr, Ordering::SeqCst);
-
-        let steals = unsafe { ptr::replace(self.queue.consumer_addition().steals.get(), 0) };
-
-        match self.queue.producer_addition().cnt.fetch_sub(1 + steals, Ordering::SeqCst) {
-            DISCONNECTED => {
-                self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst);
-            }
-            // If we factor in our steals and notice that the channel has no
-            // data, we successfully sleep
-            n => {
-                assert!(n >= 0);
-                if n - steals <= 0 {
-                    return Ok(());
-                }
-            }
-        }
-
-        self.queue.producer_addition().to_wake.store(EMPTY, Ordering::SeqCst);
-        Err(unsafe { SignalToken::from_raw(ptr) })
-    }
-
-    pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure<T>> {
-        // Optimistic preflight check (scheduling is expensive).
-        match self.try_recv() {
-            Err(Empty) => {}
-            data => return data,
-        }
-
-        // Welp, our channel has no data. Deschedule the current thread and
-        // initiate the blocking protocol.
-        let (wait_token, signal_token) = blocking::tokens();
-        if self.decrement(signal_token).is_ok() {
-            if let Some(deadline) = deadline {
-                let timed_out = !wait_token.wait_max_until(deadline);
-                if timed_out {
-                    self.abort_selection(/* was_upgrade = */ false).map_err(Upgraded)?;
-                }
-            } else {
-                wait_token.wait();
-            }
-        }
-
-        match self.try_recv() {
-            // Messages which actually popped from the queue shouldn't count as
-            // a steal, so offset the decrement here (we already have our
-            // "steal" factored into the channel count above).
-            data @ (Ok(..) | Err(Upgraded(..))) => unsafe {
-                *self.queue.consumer_addition().steals.get() -= 1;
-                data
-            },
-
-            data => data,
-        }
-    }
-
-    pub fn try_recv(&self) -> Result<T, Failure<T>> {
-        match self.queue.pop() {
-            // If we stole some data, record to that effect (this will be
-            // factored into cnt later on).
-            //
-            // Note that we don't allow steals to grow without bound in order to
-            // prevent eventual overflow of either steals or cnt as an overflow
-            // would have catastrophic results. Sometimes, steals > cnt, but
-            // other times cnt > steals, so we don't know the relation between
-            // steals and cnt. This code path is executed only rarely, so we do
-            // a pretty slow operation, of swapping 0 into cnt, taking steals
-            // down as much as possible (without going negative), and then
-            // adding back in whatever we couldn't factor into steals.
-            Some(data) => unsafe {
-                if *self.queue.consumer_addition().steals.get() > MAX_STEALS {
-                    match self.queue.producer_addition().cnt.swap(0, Ordering::SeqCst) {
-                        DISCONNECTED => {
-                            self.queue
-                                .producer_addition()
-                                .cnt
-                                .store(DISCONNECTED, Ordering::SeqCst);
-                        }
-                        n => {
-                            let m = cmp::min(n, *self.queue.consumer_addition().steals.get());
-                            *self.queue.consumer_addition().steals.get() -= m;
-                            self.bump(n - m);
-                        }
-                    }
-                    assert!(*self.queue.consumer_addition().steals.get() >= 0);
-                }
-                *self.queue.consumer_addition().steals.get() += 1;
-                match data {
-                    Data(t) => Ok(t),
-                    GoUp(up) => Err(Upgraded(up)),
-                }
-            },
-
-            None => {
-                match self.queue.producer_addition().cnt.load(Ordering::SeqCst) {
-                    n if n != DISCONNECTED => Err(Empty),
-
-                    // This is a little bit of a tricky case. We failed to pop
-                    // data above, and then we have viewed that the channel is
-                    // disconnected. In this window more data could have been
-                    // sent on the channel. It doesn't really make sense to
-                    // return that the channel is disconnected when there's
-                    // actually data on it, so be extra sure there's no data by
-                    // popping one more time.
-                    //
-                    // We can ignore steals because the other end is
-                    // disconnected and we'll never need to really factor in our
-                    // steals again.
-                    _ => match self.queue.pop() {
-                        Some(Data(t)) => Ok(t),
-                        Some(GoUp(up)) => Err(Upgraded(up)),
-                        None => Err(Disconnected),
-                    },
-                }
-            }
-        }
-    }
-
-    pub fn drop_chan(&self) {
-        // Dropping a channel is pretty simple, we just flag it as disconnected
-        // and then wakeup a blocker if there is one.
-        match self.queue.producer_addition().cnt.swap(DISCONNECTED, Ordering::SeqCst) {
-            -1 => {
-                self.take_to_wake().signal();
-            }
-            DISCONNECTED => {}
-            n => {
-                assert!(n >= 0);
-            }
-        }
-    }
-
-    pub fn drop_port(&self) {
-        // Dropping a port seems like a fairly trivial thing. In theory all we
-        // need to do is flag that we're disconnected and then everything else
-        // can take over (we don't have anyone to wake up).
-        //
-        // The catch for Ports is that we want to drop the entire contents of
-        // the queue. There are multiple reasons for having this property, the
-        // largest of which is that if another chan is waiting in this channel
-        // (but not received yet), then waiting on that port will cause a
-        // deadlock.
-        //
-        // So if we accept that we must now destroy the entire contents of the
-        // queue, this code may make a bit more sense. The tricky part is that
-        // we can't let any in-flight sends go un-dropped, we have to make sure
-        // *everything* is dropped and nothing new will come onto the channel.
-
-        // The first thing we do is set a flag saying that we're done for. All
-        // sends are gated on this flag, so we're immediately guaranteed that
-        // there are a bounded number of active sends that we'll have to deal
-        // with.
-        self.queue.producer_addition().port_dropped.store(true, Ordering::SeqCst);
-
-        // Now that we're guaranteed to deal with a bounded number of senders,
-        // we need to drain the queue. This draining process happens atomically
-        // with respect to the "count" of the channel. If the count is nonzero
-        // (with steals taken into account), then there must be data on the
-        // channel. In this case we drain everything and then try again. We will
-        // continue to fail while active senders send data while we're dropping
-        // data, but eventually we're guaranteed to break out of this loop
-        // (because there is a bounded number of senders).
-        let mut steals = unsafe { *self.queue.consumer_addition().steals.get() };
-        while {
-            match self.queue.producer_addition().cnt.compare_exchange(
-                steals,
-                DISCONNECTED,
-                Ordering::SeqCst,
-                Ordering::SeqCst,
-            ) {
-                Ok(_) => false,
-                Err(old) => old != DISCONNECTED,
-            }
-        } {
-            while self.queue.pop().is_some() {
-                steals += 1;
-            }
-        }
-
-        // At this point in time, we have gated all future senders from sending,
-        // and we have flagged the channel as being disconnected. The senders
-        // still have some responsibility, however, because some sends might not
-        // complete until after we flag the disconnection. There are more
-        // details in the sending methods that see DISCONNECTED
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // select implementation
-    ////////////////////////////////////////////////////////////////////////////
-
-    // increment the count on the channel (used for selection)
-    fn bump(&self, amt: isize) -> isize {
-        match self.queue.producer_addition().cnt.fetch_add(amt, Ordering::SeqCst) {
-            DISCONNECTED => {
-                self.queue.producer_addition().cnt.store(DISCONNECTED, Ordering::SeqCst);
-                DISCONNECTED
-            }
-            n => n,
-        }
-    }
-
-    // Removes a previous thread from being blocked in this port
-    pub fn abort_selection(&self, was_upgrade: bool) -> Result<bool, Receiver<T>> {
-        // If we're aborting selection after upgrading from a oneshot, then
-        // we're guarantee that no one is waiting. The only way that we could
-        // have seen the upgrade is if data was actually sent on the channel
-        // half again. For us, this means that there is guaranteed to be data on
-        // this channel. Furthermore, we're guaranteed that there was no
-        // start_selection previously, so there's no need to modify `self.cnt`
-        // at all.
-        //
-        // Hence, because of these invariants, we immediately return `Ok(true)`.
-        // Note that the data might not actually be sent on the channel just yet.
-        // The other end could have flagged the upgrade but not sent data to
-        // this end. This is fine because we know it's a small bounded windows
-        // of time until the data is actually sent.
-        if was_upgrade {
-            assert_eq!(unsafe { *self.queue.consumer_addition().steals.get() }, 0);
-            assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY);
-            return Ok(true);
-        }
-
-        // We want to make sure that the count on the channel goes non-negative,
-        // and in the stream case we can have at most one steal, so just assume
-        // that we had one steal.
-        let steals = 1;
-        let prev = self.bump(steals + 1);
-
-        // If we were previously disconnected, then we know for sure that there
-        // is no thread in to_wake, so just keep going
-        let has_data = if prev == DISCONNECTED {
-            assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY);
-            true // there is data, that data is that we're disconnected
-        } else {
-            let cur = prev + steals + 1;
-            assert!(cur >= 0);
-
-            // If the previous count was negative, then we just made things go
-            // positive, hence we passed the -1 boundary and we're responsible
-            // for removing the to_wake() field and trashing it.
-            //
-            // If the previous count was positive then we're in a tougher
-            // situation. A possible race is that a sender just incremented
-            // through -1 (meaning it's going to try to wake a thread up), but it
-            // hasn't yet read the to_wake. In order to prevent a future recv()
-            // from waking up too early (this sender picking up the plastered
-            // over to_wake), we spin loop here waiting for to_wake to be 0.
-            // Note that this entire select() implementation needs an overhaul,
-            // and this is *not* the worst part of it, so this is not done as a
-            // final solution but rather out of necessity for now to get
-            // something working.
-            if prev < 0 {
-                drop(self.take_to_wake());
-            } else {
-                while self.queue.producer_addition().to_wake.load(Ordering::SeqCst) != EMPTY {
-                    thread::yield_now();
-                }
-            }
-            unsafe {
-                assert_eq!(*self.queue.consumer_addition().steals.get(), 0);
-                *self.queue.consumer_addition().steals.get() = steals;
-            }
-
-            // if we were previously positive, then there's surely data to
-            // receive
-            prev >= 0
-        };
-
-        // Now that we've determined that this queue "has data", we peek at the
-        // queue to see if the data is an upgrade or not. If it's an upgrade,
-        // then we need to destroy this port and abort selection on the
-        // upgraded port.
-        if has_data {
-            match self.queue.peek() {
-                Some(&mut GoUp(..)) => match self.queue.pop() {
-                    Some(GoUp(port)) => Err(port),
-                    _ => unreachable!(),
-                },
-                _ => Ok(true),
-            }
-        } else {
-            Ok(false)
-        }
-    }
-}
-
-impl<T> Drop for Packet<T> {
-    fn drop(&mut self) {
-        // Note that this load is not only an assert for correctness about
-        // disconnection, but also a proper fence before the read of
-        // `to_wake`, so this assert cannot be removed with also removing
-        // the `to_wake` assert.
-        assert_eq!(self.queue.producer_addition().cnt.load(Ordering::SeqCst), DISCONNECTED);
-        assert_eq!(self.queue.producer_addition().to_wake.load(Ordering::SeqCst), EMPTY);
-    }
-}
diff --git a/library/std/src/sync/mpsc/sync.rs b/library/std/src/sync/mpsc/sync.rs
deleted file mode 100644
index 733761671a0..00000000000
--- a/library/std/src/sync/mpsc/sync.rs
+++ /dev/null
@@ -1,495 +0,0 @@
-use self::Blocker::*;
-/// Synchronous channels/ports
-///
-/// This channel implementation differs significantly from the asynchronous
-/// implementations found next to it (oneshot/stream/share). This is an
-/// implementation of a synchronous, bounded buffer channel.
-///
-/// Each channel is created with some amount of backing buffer, and sends will
-/// *block* until buffer space becomes available. A buffer size of 0 is valid,
-/// which means that every successful send is paired with a successful recv.
-///
-/// This flavor of channels defines a new `send_opt` method for channels which
-/// is the method by which a message is sent but the thread does not panic if it
-/// cannot be delivered.
-///
-/// Another major difference is that send() will *always* return back the data
-/// if it couldn't be sent. This is because it is deterministically known when
-/// the data is received and when it is not received.
-///
-/// Implementation-wise, it can all be summed up with "use a mutex plus some
-/// logic". The mutex used here is an OS native mutex, meaning that no user code
-/// is run inside of the mutex (to prevent context switching). This
-/// implementation shares almost all code for the buffered and unbuffered cases
-/// of a synchronous channel. There are a few branches for the unbuffered case,
-/// but they're mostly just relevant to blocking senders.
-pub use self::Failure::*;
-
-use core::intrinsics::abort;
-use core::mem;
-use core::ptr;
-
-use crate::sync::atomic::{AtomicUsize, Ordering};
-use crate::sync::mpsc::blocking::{self, SignalToken, WaitToken};
-use crate::sync::{Mutex, MutexGuard};
-use crate::time::Instant;
-
-const MAX_REFCOUNT: usize = (isize::MAX) as usize;
-
-pub struct Packet<T> {
-    /// Only field outside of the mutex. Just done for kicks, but mainly because
-    /// the other shared channel already had the code implemented
-    channels: AtomicUsize,
-
-    lock: Mutex<State<T>>,
-}
-
-unsafe impl<T: Send> Send for Packet<T> {}
-
-unsafe impl<T: Send> Sync for Packet<T> {}
-
-struct State<T> {
-    disconnected: bool, // Is the channel disconnected yet?
-    queue: Queue,       // queue of senders waiting to send data
-    blocker: Blocker,   // currently blocked thread on this channel
-    buf: Buffer<T>,     // storage for buffered messages
-    cap: usize,         // capacity of this channel
-
-    /// A curious flag used to indicate whether a sender failed or succeeded in
-    /// blocking. This is used to transmit information back to the thread that it
-    /// must dequeue its message from the buffer because it was not received.
-    /// This is only relevant in the 0-buffer case. This obviously cannot be
-    /// safely constructed, but it's guaranteed to always have a valid pointer
-    /// value.
-    canceled: Option<&'static mut bool>,
-}
-
-unsafe impl<T: Send> Send for State<T> {}
-
-/// Possible flavors of threads who can be blocked on this channel.
-enum Blocker {
-    BlockedSender(SignalToken),
-    BlockedReceiver(SignalToken),
-    NoneBlocked,
-}
-
-/// Simple queue for threading threads together. Nodes are stack-allocated, so
-/// this structure is not safe at all
-struct Queue {
-    head: *mut Node,
-    tail: *mut Node,
-}
-
-struct Node {
-    token: Option<SignalToken>,
-    next: *mut Node,
-}
-
-unsafe impl Send for Node {}
-
-/// A simple ring-buffer
-struct Buffer<T> {
-    buf: Vec<Option<T>>,
-    start: usize,
-    size: usize,
-}
-
-#[derive(Debug)]
-pub enum Failure {
-    Empty,
-    Disconnected,
-}
-
-/// Atomically blocks the current thread, placing it into `slot`, unlocking `lock`
-/// in the meantime. This re-locks the mutex upon returning.
-fn wait<'a, 'b, T>(
-    lock: &'a Mutex<State<T>>,
-    mut guard: MutexGuard<'b, State<T>>,
-    f: fn(SignalToken) -> Blocker,
-) -> MutexGuard<'a, State<T>> {
-    let (wait_token, signal_token) = blocking::tokens();
-    match mem::replace(&mut guard.blocker, f(signal_token)) {
-        NoneBlocked => {}
-        _ => unreachable!(),
-    }
-    drop(guard); // unlock
-    wait_token.wait(); // block
-    lock.lock().unwrap() // relock
-}
-
-/// Same as wait, but waiting at most until `deadline`.
-fn wait_timeout_receiver<'a, 'b, T>(
-    lock: &'a Mutex<State<T>>,
-    deadline: Instant,
-    mut guard: MutexGuard<'b, State<T>>,
-    success: &mut bool,
-) -> MutexGuard<'a, State<T>> {
-    let (wait_token, signal_token) = blocking::tokens();
-    match mem::replace(&mut guard.blocker, BlockedReceiver(signal_token)) {
-        NoneBlocked => {}
-        _ => unreachable!(),
-    }
-    drop(guard); // unlock
-    *success = wait_token.wait_max_until(deadline); // block
-    let mut new_guard = lock.lock().unwrap(); // relock
-    if !*success {
-        abort_selection(&mut new_guard);
-    }
-    new_guard
-}
-
-fn abort_selection<T>(guard: &mut MutexGuard<'_, State<T>>) -> bool {
-    match mem::replace(&mut guard.blocker, NoneBlocked) {
-        NoneBlocked => true,
-        BlockedSender(token) => {
-            guard.blocker = BlockedSender(token);
-            true
-        }
-        BlockedReceiver(token) => {
-            drop(token);
-            false
-        }
-    }
-}
-
-/// Wakes up a thread, dropping the lock at the correct time
-fn wakeup<T>(token: SignalToken, guard: MutexGuard<'_, State<T>>) {
-    // We need to be careful to wake up the waiting thread *outside* of the mutex
-    // in case it incurs a context switch.
-    drop(guard);
-    token.signal();
-}
-
-impl<T> Packet<T> {
-    pub fn new(capacity: usize) -> Packet<T> {
-        Packet {
-            channels: AtomicUsize::new(1),
-            lock: Mutex::new(State {
-                disconnected: false,
-                blocker: NoneBlocked,
-                cap: capacity,
-                canceled: None,
-                queue: Queue { head: ptr::null_mut(), tail: ptr::null_mut() },
-                buf: Buffer {
-                    buf: (0..capacity + if capacity == 0 { 1 } else { 0 }).map(|_| None).collect(),
-                    start: 0,
-                    size: 0,
-                },
-            }),
-        }
-    }
-
-    // wait until a send slot is available, returning locked access to
-    // the channel state.
-    fn acquire_send_slot(&self) -> MutexGuard<'_, State<T>> {
-        let mut node = Node { token: None, next: ptr::null_mut() };
-        loop {
-            let mut guard = self.lock.lock().unwrap();
-            // are we ready to go?
-            if guard.disconnected || guard.buf.size() < guard.buf.capacity() {
-                return guard;
-            }
-            // no room; actually block
-            let wait_token = guard.queue.enqueue(&mut node);
-            drop(guard);
-            wait_token.wait();
-        }
-    }
-
-    pub fn send(&self, t: T) -> Result<(), T> {
-        let mut guard = self.acquire_send_slot();
-        if guard.disconnected {
-            return Err(t);
-        }
-        guard.buf.enqueue(t);
-
-        match mem::replace(&mut guard.blocker, NoneBlocked) {
-            // if our capacity is 0, then we need to wait for a receiver to be
-            // available to take our data. After waiting, we check again to make
-            // sure the port didn't go away in the meantime. If it did, we need
-            // to hand back our data.
-            NoneBlocked if guard.cap == 0 => {
-                let mut canceled = false;
-                assert!(guard.canceled.is_none());
-                guard.canceled = Some(unsafe { mem::transmute(&mut canceled) });
-                let mut guard = wait(&self.lock, guard, BlockedSender);
-                if canceled { Err(guard.buf.dequeue()) } else { Ok(()) }
-            }
-
-            // success, we buffered some data
-            NoneBlocked => Ok(()),
-
-            // success, someone's about to receive our buffered data.
-            BlockedReceiver(token) => {
-                wakeup(token, guard);
-                Ok(())
-            }
-
-            BlockedSender(..) => panic!("lolwut"),
-        }
-    }
-
-    pub fn try_send(&self, t: T) -> Result<(), super::TrySendError<T>> {
-        let mut guard = self.lock.lock().unwrap();
-        if guard.disconnected {
-            Err(super::TrySendError::Disconnected(t))
-        } else if guard.buf.size() == guard.buf.capacity() {
-            Err(super::TrySendError::Full(t))
-        } else if guard.cap == 0 {
-            // With capacity 0, even though we have buffer space we can't
-            // transfer the data unless there's a receiver waiting.
-            match mem::replace(&mut guard.blocker, NoneBlocked) {
-                NoneBlocked => Err(super::TrySendError::Full(t)),
-                BlockedSender(..) => unreachable!(),
-                BlockedReceiver(token) => {
-                    guard.buf.enqueue(t);
-                    wakeup(token, guard);
-                    Ok(())
-                }
-            }
-        } else {
-            // If the buffer has some space and the capacity isn't 0, then we
-            // just enqueue the data for later retrieval, ensuring to wake up
-            // any blocked receiver if there is one.
-            assert!(guard.buf.size() < guard.buf.capacity());
-            guard.buf.enqueue(t);
-            match mem::replace(&mut guard.blocker, NoneBlocked) {
-                BlockedReceiver(token) => wakeup(token, guard),
-                NoneBlocked => {}
-                BlockedSender(..) => unreachable!(),
-            }
-            Ok(())
-        }
-    }
-
-    // Receives a message from this channel
-    //
-    // When reading this, remember that there can only ever be one receiver at
-    // time.
-    pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure> {
-        let mut guard = self.lock.lock().unwrap();
-
-        let mut woke_up_after_waiting = false;
-        // Wait for the buffer to have something in it. No need for a
-        // while loop because we're the only receiver.
-        if !guard.disconnected && guard.buf.size() == 0 {
-            if let Some(deadline) = deadline {
-                guard =
-                    wait_timeout_receiver(&self.lock, deadline, guard, &mut woke_up_after_waiting);
-            } else {
-                guard = wait(&self.lock, guard, BlockedReceiver);
-                woke_up_after_waiting = true;
-            }
-        }
-
-        // N.B., channel could be disconnected while waiting, so the order of
-        // these conditionals is important.
-        if guard.disconnected && guard.buf.size() == 0 {
-            return Err(Disconnected);
-        }
-
-        // Pick up the data, wake up our neighbors, and carry on
-        assert!(guard.buf.size() > 0 || (deadline.is_some() && !woke_up_after_waiting));
-
-        if guard.buf.size() == 0 {
-            return Err(Empty);
-        }
-
-        let ret = guard.buf.dequeue();
-        self.wakeup_senders(woke_up_after_waiting, guard);
-        Ok(ret)
-    }
-
-    pub fn try_recv(&self) -> Result<T, Failure> {
-        let mut guard = self.lock.lock().unwrap();
-
-        // Easy cases first
-        if guard.disconnected && guard.buf.size() == 0 {
-            return Err(Disconnected);
-        }
-        if guard.buf.size() == 0 {
-            return Err(Empty);
-        }
-
-        // Be sure to wake up neighbors
-        let ret = Ok(guard.buf.dequeue());
-        self.wakeup_senders(false, guard);
-        ret
-    }
-
-    // Wake up pending senders after some data has been received
-    //
-    // * `waited` - flag if the receiver blocked to receive some data, or if it
-    //              just picked up some data on the way out
-    // * `guard` - the lock guard that is held over this channel's lock
-    fn wakeup_senders(&self, waited: bool, mut guard: MutexGuard<'_, State<T>>) {
-        let pending_sender1: Option<SignalToken> = guard.queue.dequeue();
-
-        // If this is a no-buffer channel (cap == 0), then if we didn't wait we
-        // need to ACK the sender. If we waited, then the sender waking us up
-        // was already the ACK.
-        let pending_sender2 = if guard.cap == 0 && !waited {
-            match mem::replace(&mut guard.blocker, NoneBlocked) {
-                NoneBlocked => None,
-                BlockedReceiver(..) => unreachable!(),
-                BlockedSender(token) => {
-                    guard.canceled.take();
-                    Some(token)
-                }
-            }
-        } else {
-            None
-        };
-        mem::drop(guard);
-
-        // only outside of the lock do we wake up the pending threads
-        if let Some(token) = pending_sender1 {
-            token.signal();
-        }
-        if let Some(token) = pending_sender2 {
-            token.signal();
-        }
-    }
-
-    // Prepares this shared packet for a channel clone, essentially just bumping
-    // a refcount.
-    pub fn clone_chan(&self) {
-        let old_count = self.channels.fetch_add(1, Ordering::SeqCst);
-
-        // See comments on Arc::clone() on why we do this (for `mem::forget`).
-        if old_count > MAX_REFCOUNT {
-            abort();
-        }
-    }
-
-    pub fn drop_chan(&self) {
-        // Only flag the channel as disconnected if we're the last channel
-        match self.channels.fetch_sub(1, Ordering::SeqCst) {
-            1 => {}
-            _ => return,
-        }
-
-        // Not much to do other than wake up a receiver if one's there
-        let mut guard = self.lock.lock().unwrap();
-        if guard.disconnected {
-            return;
-        }
-        guard.disconnected = true;
-        match mem::replace(&mut guard.blocker, NoneBlocked) {
-            NoneBlocked => {}
-            BlockedSender(..) => unreachable!(),
-            BlockedReceiver(token) => wakeup(token, guard),
-        }
-    }
-
-    pub fn drop_port(&self) {
-        let mut guard = self.lock.lock().unwrap();
-
-        if guard.disconnected {
-            return;
-        }
-        guard.disconnected = true;
-
-        // If the capacity is 0, then the sender may want its data back after
-        // we're disconnected. Otherwise it's now our responsibility to destroy
-        // the buffered data. As with many other portions of this code, this
-        // needs to be careful to destroy the data *outside* of the lock to
-        // prevent deadlock.
-        let _data = if guard.cap != 0 { mem::take(&mut guard.buf.buf) } else { Vec::new() };
-        let mut queue =
-            mem::replace(&mut guard.queue, Queue { head: ptr::null_mut(), tail: ptr::null_mut() });
-
-        let waiter = match mem::replace(&mut guard.blocker, NoneBlocked) {
-            NoneBlocked => None,
-            BlockedSender(token) => {
-                *guard.canceled.take().unwrap() = true;
-                Some(token)
-            }
-            BlockedReceiver(..) => unreachable!(),
-        };
-        mem::drop(guard);
-
-        while let Some(token) = queue.dequeue() {
-            token.signal();
-        }
-        if let Some(token) = waiter {
-            token.signal();
-        }
-    }
-}
-
-impl<T> Drop for Packet<T> {
-    fn drop(&mut self) {
-        assert_eq!(self.channels.load(Ordering::SeqCst), 0);
-        let mut guard = self.lock.lock().unwrap();
-        assert!(guard.queue.dequeue().is_none());
-        assert!(guard.canceled.is_none());
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Buffer, a simple ring buffer backed by Vec<T>
-////////////////////////////////////////////////////////////////////////////////
-
-impl<T> Buffer<T> {
-    fn enqueue(&mut self, t: T) {
-        let pos = (self.start + self.size) % self.buf.len();
-        self.size += 1;
-        let prev = mem::replace(&mut self.buf[pos], Some(t));
-        assert!(prev.is_none());
-    }
-
-    fn dequeue(&mut self) -> T {
-        let start = self.start;
-        self.size -= 1;
-        self.start = (self.start + 1) % self.buf.len();
-        let result = &mut self.buf[start];
-        result.take().unwrap()
-    }
-
-    fn size(&self) -> usize {
-        self.size
-    }
-    fn capacity(&self) -> usize {
-        self.buf.len()
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Queue, a simple queue to enqueue threads with (stack-allocated nodes)
-////////////////////////////////////////////////////////////////////////////////
-
-impl Queue {
-    fn enqueue(&mut self, node: &mut Node) -> WaitToken {
-        let (wait_token, signal_token) = blocking::tokens();
-        node.token = Some(signal_token);
-        node.next = ptr::null_mut();
-
-        if self.tail.is_null() {
-            self.head = node as *mut Node;
-            self.tail = node as *mut Node;
-        } else {
-            unsafe {
-                (*self.tail).next = node as *mut Node;
-                self.tail = node as *mut Node;
-            }
-        }
-
-        wait_token
-    }
-
-    fn dequeue(&mut self) -> Option<SignalToken> {
-        if self.head.is_null() {
-            return None;
-        }
-        let node = self.head;
-        self.head = unsafe { (*node).next };
-        if self.head.is_null() {
-            self.tail = ptr::null_mut();
-        }
-        unsafe {
-            (*node).next = ptr::null_mut();
-            Some((*node).token.take().unwrap())
-        }
-    }
-}
diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs
index f6d0796f604..1e52a4a705c 100644
--- a/library/std/src/sync/mpsc/tests.rs
+++ b/library/std/src/sync/mpsc/tests.rs
@@ -706,3 +706,18 @@ fn issue_32114() {
     let _ = tx.send(123);
     assert_eq!(tx.send(123), Err(SendError(123)));
 }
+
+#[test]
+fn issue_39364() {
+    let (tx, rx) = channel::<()>();
+    let t = thread::spawn(move || {
+        thread::sleep(Duration::from_millis(300));
+        let _ = tx.clone();
+        // Don't drop; hand back to caller.
+        tx
+    });
+
+    let _ = rx.recv_timeout(Duration::from_millis(500));
+    let _tx = t.join().unwrap(); // delay dropping until end of test
+    let _ = rx.recv_timeout(Duration::from_millis(500));
+}
diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs
index 1df1ca758c0..80dc4c038d6 100644
--- a/library/std/src/thread/local/tests.rs
+++ b/library/std/src/thread/local/tests.rs
@@ -1,15 +1,34 @@
 use crate::cell::{Cell, UnsafeCell};
 use crate::sync::atomic::{AtomicU8, Ordering};
-use crate::sync::mpsc::{channel, Sender};
+use crate::sync::{Arc, Condvar, Mutex};
 use crate::thread::{self, LocalKey};
 use crate::thread_local;
 
-struct Foo(Sender<()>);
+#[derive(Clone, Default)]
+struct Signal(Arc<(Mutex<bool>, Condvar)>);
+
+impl Signal {
+    fn notify(&self) {
+        let (set, cvar) = &*self.0;
+        *set.lock().unwrap() = true;
+        cvar.notify_one();
+    }
+
+    fn wait(&self) {
+        let (set, cvar) = &*self.0;
+        let mut set = set.lock().unwrap();
+        while !*set {
+            set = cvar.wait(set).unwrap();
+        }
+    }
+}
+
+struct Foo(Signal);
 
 impl Drop for Foo {
     fn drop(&mut self) {
-        let Foo(ref s) = *self;
-        s.send(()).unwrap();
+        let Foo(ref f) = *self;
+        f.notify();
     }
 }
 
@@ -69,14 +88,15 @@ fn smoke_dtor() {
     run(&FOO2);
 
     fn run(key: &'static LocalKey<UnsafeCell<Option<Foo>>>) {
-        let (tx, rx) = channel();
+        let signal = Signal::default();
+        let signal2 = signal.clone();
         let t = thread::spawn(move || unsafe {
-            let mut tx = Some(tx);
+            let mut signal = Some(signal2);
             key.with(|f| {
-                *f.get() = Some(Foo(tx.take().unwrap()));
+                *f.get() = Some(Foo(signal.take().unwrap()));
             });
         });
-        rx.recv().unwrap();
+        signal.wait();
         t.join().unwrap();
     }
 }
@@ -165,48 +185,50 @@ fn self_referential() {
 // requires the destructor to be run to pass the test).
 #[test]
 fn dtors_in_dtors_in_dtors() {
-    struct S1(Sender<()>);
+    struct S1(Signal);
     thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None));
     thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell::new(None));
 
     impl Drop for S1 {
         fn drop(&mut self) {
-            let S1(ref tx) = *self;
+            let S1(ref signal) = *self;
             unsafe {
-                let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
+                let _ = K2.try_with(|s| *s.get() = Some(Foo(signal.clone())));
             }
         }
     }
 
-    let (tx, rx) = channel();
+    let signal = Signal::default();
+    let signal2 = signal.clone();
     let _t = thread::spawn(move || unsafe {
-        let mut tx = Some(tx);
-        K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
+        let mut signal = Some(signal2);
+        K1.with(|s| *s.get() = Some(S1(signal.take().unwrap())));
     });
-    rx.recv().unwrap();
+    signal.wait();
 }
 
 #[test]
 fn dtors_in_dtors_in_dtors_const_init() {
-    struct S1(Sender<()>);
+    struct S1(Signal);
     thread_local!(static K1: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) });
     thread_local!(static K2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) });
 
     impl Drop for S1 {
         fn drop(&mut self) {
-            let S1(ref tx) = *self;
+            let S1(ref signal) = *self;
             unsafe {
-                let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone())));
+                let _ = K2.try_with(|s| *s.get() = Some(Foo(signal.clone())));
             }
         }
     }
 
-    let (tx, rx) = channel();
+    let signal = Signal::default();
+    let signal2 = signal.clone();
     let _t = thread::spawn(move || unsafe {
-        let mut tx = Some(tx);
-        K1.with(|s| *s.get() = Some(S1(tx.take().unwrap())));
+        let mut signal = Some(signal2);
+        K1.with(|s| *s.get() = Some(S1(signal.take().unwrap())));
     });
-    rx.recv().unwrap();
+    signal.wait();
 }
 
 // This test tests that TLS destructors have run before the thread joins. The
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index 34e18b5fa87..ecd06ebf743 100644
--- a/library/std/src/time.rs
+++ b/library/std/src/time.rs
@@ -43,7 +43,7 @@ use crate::sys_common::{FromInner, IntoInner};
 #[stable(feature = "time", since = "1.3.0")]
 pub use core::time::Duration;
 
-#[stable(feature = "duration_checked_float", since = "CURRENT_RUSTC_VERSION")]
+#[stable(feature = "duration_checked_float", since = "1.66.0")]
 pub use core::time::TryFromFloatSecsError;
 
 /// A measurement of a monotonically nondecreasing clock.
diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml
index 32c4a7eb5c1..eab2717c452 100644
--- a/library/unwind/Cargo.toml
+++ b/library/unwind/Cargo.toml
@@ -20,7 +20,7 @@ compiler_builtins = "0.1.0"
 cfg-if = "1.0"
 
 [build-dependencies]
-cc = "1.0.69"
+cc = "1.0.76"
 
 [features]
 
diff --git a/library/unwind/build.rs b/library/unwind/build.rs
index 31af390253b..5c3c02fb6ad 100644
--- a/library/unwind/build.rs
+++ b/library/unwind/build.rs
@@ -21,29 +21,5 @@ fn main() {
         if has_unwind {
             println!("cargo:rustc-cfg=feature=\"system-llvm-libunwind\"");
         }
-    } else if target.contains("freebsd") {
-        println!("cargo:rustc-link-lib=gcc_s");
-    } else if target.contains("netbsd") {
-        println!("cargo:rustc-link-lib=gcc_s");
-    } else if target.contains("openbsd") {
-        if target.contains("sparc64") {
-            println!("cargo:rustc-link-lib=gcc");
-        } else {
-            println!("cargo:rustc-link-lib=c++abi");
-        }
-    } else if target.contains("solaris") {
-        println!("cargo:rustc-link-lib=gcc_s");
-    } else if target.contains("illumos") {
-        println!("cargo:rustc-link-lib=gcc_s");
-    } else if target.contains("dragonfly") {
-        println!("cargo:rustc-link-lib=gcc_pic");
-    } else if target.ends_with("pc-windows-gnu") {
-        // This is handled in the target spec with late_link_args_[static|dynamic]
-    } else if target.contains("uwp-windows-gnu") {
-        println!("cargo:rustc-link-lib=unwind");
-    } else if target.contains("haiku") {
-        println!("cargo:rustc-link-lib=gcc_s");
-    } else if target.contains("redox") {
-        // redox is handled in lib.rs
     }
 }
diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs
index 32c0ef3e116..3753071d5f0 100644
--- a/library/unwind/src/lib.rs
+++ b/library/unwind/src/lib.rs
@@ -104,6 +104,26 @@ extern "C" {}
 #[link(name = "unwind", kind = "static", modifiers = "-bundle")]
 extern "C" {}
 
-#[cfg(all(target_os = "windows", target_env = "gnu", target_abi = "llvm"))]
-#[link(name = "unwind", kind = "static", modifiers = "-bundle")]
+#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
+#[link(name = "gcc_s")]
+extern "C" {}
+
+#[cfg(all(target_os = "openbsd", target_arch = "sparc64"))]
+#[link(name = "gcc")]
+extern "C" {}
+
+#[cfg(all(target_os = "openbsd", not(target_arch = "sparc64")))]
+#[link(name = "c++abi")]
+extern "C" {}
+
+#[cfg(any(target_os = "solaris", target_os = "illumos"))]
+#[link(name = "gcc_s")]
+extern "C" {}
+
+#[cfg(target_os = "dragonfly")]
+#[link(name = "gcc_pic")]
+extern "C" {}
+
+#[cfg(target_os = "haiku")]
+#[link(name = "gcc_s")]
 extern "C" {}
diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs
index a5b6193b086..15500f7fd35 100644
--- a/library/unwind/src/libunwind.rs
+++ b/library/unwind/src/libunwind.rs
@@ -36,9 +36,12 @@ pub const unwinder_private_data_size: usize = 20;
 #[cfg(all(target_arch = "arm", any(target_os = "ios", target_os = "watchos")))]
 pub const unwinder_private_data_size: usize = 5;
 
-#[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))]
+#[cfg(all(target_arch = "aarch64", target_pointer_width = "64", not(target_os = "windows")))]
 pub const unwinder_private_data_size: usize = 2;
 
+#[cfg(all(target_arch = "aarch64", target_pointer_width = "64", target_os = "windows"))]
+pub const unwinder_private_data_size: usize = 6;
+
 #[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))]
 pub const unwinder_private_data_size: usize = 5;
 
@@ -90,7 +93,10 @@ pub type _Unwind_Exception_Cleanup_Fn =
 // rustc_codegen_ssa::src::back::symbol_export, rustc_middle::middle::exported_symbols
 // and RFC 2841
 #[cfg_attr(
-    all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")),
+    any(
+        all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")),
+        all(target_os = "windows", target_env = "gnu", target_abi = "llvm")
+    ),
     link(name = "unwind", kind = "static", modifiers = "-bundle")
 )]
 extern "C-unwind" {
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 31158870f39..8d999302a6d 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -2,14 +2,13 @@ use std::any::{type_name, Any};
 use std::cell::{Cell, RefCell};
 use std::collections::BTreeSet;
 use std::env;
-use std::ffi::{OsStr, OsString};
+use std::ffi::OsStr;
 use std::fmt::{Debug, Write};
-use std::fs::{self, File};
+use std::fs::{self};
 use std::hash::Hash;
-use std::io::{BufRead, BufReader, ErrorKind};
 use std::ops::Deref;
 use std::path::{Component, Path, PathBuf};
-use std::process::{Command, Stdio};
+use std::process::Command;
 use std::time::{Duration, Instant};
 
 use crate::cache::{Cache, Interned, INTERNER};
@@ -24,14 +23,12 @@ use crate::test;
 use crate::tool::{self, SourceType};
 use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
 use crate::EXTRA_CHECK_CFGS;
-use crate::{check, Config};
-use crate::{compile, Crate};
+use crate::{check, compile, Crate};
 use crate::{Build, CLang, DocTests, GitRepo, Mode};
 
 pub use crate::Compiler;
 // FIXME: replace with std::lazy after it gets stabilized and reaches beta
-use once_cell::sync::{Lazy, OnceCell};
-use xz2::bufread::XzDecoder;
+use once_cell::sync::Lazy;
 
 pub struct Builder<'a> {
     pub build: &'a Build,
@@ -853,241 +850,6 @@ impl<'a> Builder<'a> {
         StepDescription::run(v, self, paths);
     }
 
-    /// Modifies the interpreter section of 'fname' to fix the dynamic linker,
-    /// or the RPATH section, to fix the dynamic library search path
-    ///
-    /// This is only required on NixOS and uses the PatchELF utility to
-    /// change the interpreter/RPATH of ELF executables.
-    ///
-    /// Please see https://nixos.org/patchelf.html for more information
-    pub(crate) fn fix_bin_or_dylib(&self, fname: &Path) {
-        // FIXME: cache NixOS detection?
-        match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
-            Err(_) => return,
-            Ok(output) if !output.status.success() => return,
-            Ok(output) => {
-                let mut s = output.stdout;
-                if s.last() == Some(&b'\n') {
-                    s.pop();
-                }
-                if s != b"Linux" {
-                    return;
-                }
-            }
-        }
-
-        // If the user has asked binaries to be patched for Nix, then
-        // don't check for NixOS or `/lib`, just continue to the patching.
-        // NOTE: this intentionally comes after the Linux check:
-        // - patchelf only works with ELF files, so no need to run it on Mac or Windows
-        // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc.
-        if !self.config.patch_binaries_for_nix {
-            // Use `/etc/os-release` instead of `/etc/NIXOS`.
-            // The latter one does not exist on NixOS when using tmpfs as root.
-            const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""];
-            let os_release = match File::open("/etc/os-release") {
-                Err(e) if e.kind() == ErrorKind::NotFound => return,
-                Err(e) => panic!("failed to access /etc/os-release: {}", e),
-                Ok(f) => f,
-            };
-            if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) {
-                return;
-            }
-            if Path::new("/lib").exists() {
-                return;
-            }
-        }
-
-        // At this point we're pretty sure the user is running NixOS or using Nix
-        println!("info: you seem to be using Nix. Attempting to patch {}", fname.display());
-
-        // Only build `.nix-deps` once.
-        static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
-        let mut nix_build_succeeded = true;
-        let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
-            // Run `nix-build` to "build" each dependency (which will likely reuse
-            // the existing `/nix/store` copy, or at most download a pre-built copy).
-            //
-            // Importantly, we create a gc-root called `.nix-deps` in the `build/`
-            // directory, but still reference the actual `/nix/store` path in the rpath
-            // as it makes it significantly more robust against changes to the location of
-            // the `.nix-deps` location.
-            //
-            // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
-            // zlib: Needed as a system dependency of `libLLVM-*.so`.
-            // patchelf: Needed for patching ELF binaries (see doc comment above).
-            let nix_deps_dir = self.out.join(".nix-deps");
-            const NIX_EXPR: &str = "
-            with (import <nixpkgs> {});
-            symlinkJoin {
-                name = \"rust-stage0-dependencies\";
-                paths = [
-                    zlib
-                    patchelf
-                    stdenv.cc.bintools
-                ];
-            }
-            ";
-            nix_build_succeeded = self.try_run(Command::new("nix-build").args(&[
-                Path::new("-E"),
-                Path::new(NIX_EXPR),
-                Path::new("-o"),
-                &nix_deps_dir,
-            ]));
-            nix_deps_dir
-        });
-        if !nix_build_succeeded {
-            return;
-        }
-
-        let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
-        let rpath_entries = {
-            // ORIGIN is a relative default, all binary and dynamic libraries we ship
-            // appear to have this (even when `../lib` is redundant).
-            // NOTE: there are only two paths here, delimited by a `:`
-            let mut entries = OsString::from("$ORIGIN/../lib:");
-            entries.push(t!(fs::canonicalize(nix_deps_dir)));
-            entries.push("/lib");
-            entries
-        };
-        patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
-        if !fname.extension().map_or(false, |ext| ext == "so") {
-            // Finally, set the correct .interp for binaries
-            let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
-            // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
-            let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
-            patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]);
-        }
-
-        self.try_run(patchelf.arg(fname));
-    }
-
-    pub(crate) fn download_component(&self, url: &str, dest_path: &Path, help_on_error: &str) {
-        self.verbose(&format!("download {url}"));
-        // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
-        let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
-        // While bootstrap itself only supports http and https downloads, downstream forks might
-        // need to download components from other protocols. The match allows them adding more
-        // protocols without worrying about merge conflicts if we change the HTTP implementation.
-        match url.split_once("://").map(|(proto, _)| proto) {
-            Some("http") | Some("https") => {
-                self.download_http_with_retries(&tempfile, url, help_on_error)
-            }
-            Some(other) => panic!("unsupported protocol {other} in {url}"),
-            None => panic!("no protocol in {url}"),
-        }
-        t!(std::fs::rename(&tempfile, dest_path));
-    }
-
-    fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
-        println!("downloading {}", url);
-        // Try curl. If that fails and we are on windows, fallback to PowerShell.
-        let mut curl = Command::new("curl");
-        curl.args(&[
-            "-#",
-            "-y",
-            "30",
-            "-Y",
-            "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
-            "--connect-timeout",
-            "30", // timeout if cannot connect within 30 seconds
-            "--retry",
-            "3",
-            "-Sf",
-            "-o",
-        ]);
-        curl.arg(tempfile);
-        curl.arg(url);
-        if !self.check_run(&mut curl) {
-            if self.build.build.contains("windows-msvc") {
-                println!("Fallback to PowerShell");
-                for _ in 0..3 {
-                    if self.try_run(Command::new("PowerShell.exe").args(&[
-                        "/nologo",
-                        "-Command",
-                        "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
-                        &format!(
-                            "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
-                            url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"),
-                        ),
-                    ])) {
-                        return;
-                    }
-                    println!("\nspurious failure, trying again");
-                }
-            }
-            if !help_on_error.is_empty() {
-                eprintln!("{}", help_on_error);
-            }
-            crate::detail_exit(1);
-        }
-    }
-
-    pub(crate) fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
-        println!("extracting {} to {}", tarball.display(), dst.display());
-        if !dst.exists() {
-            t!(fs::create_dir_all(dst));
-        }
-
-        // `tarball` ends with `.tar.xz`; strip that suffix
-        // example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
-        let uncompressed_filename =
-            Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
-        let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());
-
-        // decompress the file
-        let data = t!(File::open(tarball));
-        let decompressor = XzDecoder::new(BufReader::new(data));
-
-        let mut tar = tar::Archive::new(decompressor);
-        for member in t!(tar.entries()) {
-            let mut member = t!(member);
-            let original_path = t!(member.path()).into_owned();
-            // skip the top-level directory
-            if original_path == directory_prefix {
-                continue;
-            }
-            let mut short_path = t!(original_path.strip_prefix(directory_prefix));
-            if !short_path.starts_with(pattern) {
-                continue;
-            }
-            short_path = t!(short_path.strip_prefix(pattern));
-            let dst_path = dst.join(short_path);
-            self.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
-            if !t!(member.unpack_in(dst)) {
-                panic!("path traversal attack ??");
-            }
-            let src_path = dst.join(original_path);
-            if src_path.is_dir() && dst_path.exists() {
-                continue;
-            }
-            t!(fs::rename(src_path, dst_path));
-        }
-        t!(fs::remove_dir_all(dst.join(directory_prefix)));
-    }
-
-    /// Returns whether the SHA256 checksum of `path` matches `expected`.
-    pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool {
-        use sha2::Digest;
-
-        self.verbose(&format!("verifying {}", path.display()));
-        let mut hasher = sha2::Sha256::new();
-        // FIXME: this is ok for rustfmt (4.1 MB large at time of writing), but it seems memory-intensive for rustc and larger components.
-        // Consider using streaming IO instead?
-        let contents = if self.config.dry_run() { vec![] } else { t!(fs::read(path)) };
-        hasher.update(&contents);
-        let found = hex::encode(hasher.finalize().as_slice());
-        let verified = found == expected;
-        if !verified && !self.config.dry_run() {
-            println!(
-                "invalid checksum: \n\
-                found:    {found}\n\
-                expected: {expected}",
-            );
-        }
-        return verified;
-    }
-
     /// Obtain a compiler at a given stage and for a given host. Explicitly does
     /// not take `Compiler` since all `Compiler` instances are meant to be
     /// obtained through this function, since it ensures that they are valid
@@ -1301,19 +1063,6 @@ impl<'a> Builder<'a> {
         None
     }
 
-    /// Convenience wrapper to allow `builder.llvm_link_shared()` instead of `builder.config.llvm_link_shared(&builder)`.
-    pub(crate) fn llvm_link_shared(&self) -> bool {
-        Config::llvm_link_shared(self)
-    }
-
-    pub(crate) fn download_rustc(&self) -> bool {
-        Config::download_rustc(self)
-    }
-
-    pub(crate) fn initial_rustfmt(&self) -> Option<PathBuf> {
-        Config::initial_rustfmt(self)
-    }
-
     /// Prepares an invocation of `cargo` to be run.
     ///
     /// This will create a `Command` that represents a pending execution of
diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs
index 258352a21a4..eae81b9fc69 100644
--- a/src/bootstrap/channel.rs
+++ b/src/bootstrap/channel.rs
@@ -13,8 +13,10 @@ use crate::util::output;
 use crate::util::t;
 use crate::Build;
 
+#[derive(Clone, Default)]
 pub enum GitInfo {
     /// This is not a git repository.
+    #[default]
     Absent,
     /// This is a git repository.
     /// If the info should be used (`ignore_git` is false), this will be
@@ -25,6 +27,7 @@ pub enum GitInfo {
     RecordedForTarball(Info),
 }
 
+#[derive(Clone)]
 pub struct Info {
     pub commit_date: String,
     pub sha: String,
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 79e07be614c..54906a4918b 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -206,7 +206,6 @@ fn copy_third_party_objects(
     }
 
     if target == "x86_64-fortanix-unknown-sgx"
-        || target.contains("pc-windows-gnullvm")
         || builder.config.llvm_libunwind(target) == LlvmLibunwind::InTree
             && (target.contains("linux") || target.contains("fuchsia"))
     {
@@ -764,10 +763,10 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
 
     cargo.env("CFG_LIBDIR_RELATIVE", libdir_relative);
 
-    if let Some(ref ver_date) = builder.rust_info.commit_date() {
+    if let Some(ref ver_date) = builder.rust_info().commit_date() {
         cargo.env("CFG_VER_DATE", ver_date);
     }
-    if let Some(ref ver_hash) = builder.rust_info.sha() {
+    if let Some(ref ver_hash) = builder.rust_info().sha() {
         cargo.env("CFG_VER_HASH", ver_hash);
     }
     if !builder.unstable_features() {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index e843bd411c1..af004aa5098 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -7,19 +7,18 @@ use std::cell::{Cell, RefCell};
 use std::cmp;
 use std::collections::{HashMap, HashSet};
 use std::env;
-use std::ffi::OsStr;
 use std::fmt;
 use std::fs;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::str::FromStr;
 
-use crate::builder::{Builder, TaskPath};
+use crate::builder::TaskPath;
 use crate::cache::{Interned, INTERNER};
-use crate::channel::GitInfo;
+use crate::channel::{self, GitInfo};
 pub use crate::flags::Subcommand;
 use crate::flags::{Color, Flags};
-use crate::util::{exe, output, program_out_of_date, t};
+use crate::util::{exe, output, t};
 use once_cell::sync::OnceCell;
 use serde::{Deserialize, Deserializer};
 
@@ -224,6 +223,7 @@ pub struct Config {
     #[cfg(test)]
     pub initial_rustfmt: RefCell<RustfmtState>,
     pub out: PathBuf,
+    pub rust_info: channel::GitInfo,
 }
 
 #[derive(Default, Deserialize)]
@@ -1204,7 +1204,7 @@ impl Config {
             config.rust_codegen_units_std = rust.codegen_units_std.map(threads_from_config);
             config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
             config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
-            config.download_rustc_commit = download_ci_rustc_commit(&config, rust.download_rustc);
+            config.download_rustc_commit = config.download_ci_rustc_commit(rust.download_rustc);
 
             config.rust_lto = rust
                 .lto
@@ -1326,6 +1326,7 @@ impl Config {
 
         let default = config.channel == "dev";
         config.ignore_git = ignore_git.unwrap_or(default);
+        config.rust_info = GitInfo::new(config.ignore_git, &config.src);
 
         let download_rustc = config.download_rustc_commit.is_some();
         // See https://github.com/rust-lang/compiler-team/issues/326
@@ -1401,8 +1402,8 @@ impl Config {
 
     /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI.
     /// Return the version it would have used for the given commit.
-    pub(crate) fn artifact_version_part(&self, builder: &Builder<'_>, commit: &str) -> String {
-        let (channel, version) = if builder.rust_info.is_managed_git_subrepository() {
+    pub(crate) fn artifact_version_part(&self, commit: &str) -> String {
+        let (channel, version) = if self.rust_info.is_managed_git_subrepository() {
             let mut channel = self.git();
             channel.arg("show").arg(format!("{}:src/ci/channel", commit));
             let channel = output(&mut channel);
@@ -1411,14 +1412,14 @@ impl Config {
             let version = output(&mut version);
             (channel.trim().to_owned(), version.trim().to_owned())
         } else {
-            let channel = fs::read_to_string(builder.src.join("src/ci/channel"));
-            let version = fs::read_to_string(builder.src.join("src/version"));
+            let channel = fs::read_to_string(self.src.join("src/ci/channel"));
+            let version = fs::read_to_string(self.src.join("src/version"));
             match (channel, version) {
                 (Ok(channel), Ok(version)) => {
                     (channel.trim().to_owned(), version.trim().to_owned())
                 }
                 (channel, version) => {
-                    let src = builder.src.display();
+                    let src = self.src.display();
                     eprintln!("error: failed to determine artifact channel and/or version");
                     eprintln!(
                         "help: consider using a git checkout or ensure these files are readable"
@@ -1477,17 +1478,17 @@ impl Config {
     ///
     /// If `false`, llvm should be linked statically.
     /// This is computed on demand since LLVM might have to first be downloaded from CI.
-    pub(crate) fn llvm_link_shared(builder: &Builder<'_>) -> bool {
-        let mut opt = builder.config.llvm_link_shared.get();
-        if opt.is_none() && builder.config.dry_run() {
+    pub(crate) fn llvm_link_shared(&self) -> bool {
+        let mut opt = self.llvm_link_shared.get();
+        if opt.is_none() && self.dry_run() {
             // just assume static for now - dynamic linking isn't supported on all platforms
             return false;
         }
 
         let llvm_link_shared = *opt.get_or_insert_with(|| {
-            if builder.config.llvm_from_ci {
-                crate::native::maybe_download_ci_llvm(builder);
-                let ci_llvm = builder.config.ci_llvm_root();
+            if self.llvm_from_ci {
+                self.maybe_download_ci_llvm();
+                let ci_llvm = self.ci_llvm_root();
                 let link_type = t!(
                     std::fs::read_to_string(ci_llvm.join("link-type.txt")),
                     format!("CI llvm missing: {}", ci_llvm.display())
@@ -1499,36 +1500,36 @@ impl Config {
                 false
             }
         });
-        builder.config.llvm_link_shared.set(opt);
+        self.llvm_link_shared.set(opt);
         llvm_link_shared
     }
 
     /// Return whether we will use a downloaded, pre-compiled version of rustc, or just build from source.
-    pub(crate) fn download_rustc(builder: &Builder<'_>) -> bool {
+    pub(crate) fn download_rustc(&self) -> bool {
         static DOWNLOAD_RUSTC: OnceCell<bool> = OnceCell::new();
-        if builder.config.dry_run() && DOWNLOAD_RUSTC.get().is_none() {
+        if self.dry_run() && DOWNLOAD_RUSTC.get().is_none() {
             // avoid trying to actually download the commit
             return false;
         }
 
-        *DOWNLOAD_RUSTC.get_or_init(|| match &builder.config.download_rustc_commit {
+        *DOWNLOAD_RUSTC.get_or_init(|| match &self.download_rustc_commit {
             None => false,
             Some(commit) => {
-                download_ci_rustc(builder, commit);
+                self.download_ci_rustc(commit);
                 true
             }
         })
     }
 
-    pub(crate) fn initial_rustfmt(builder: &Builder<'_>) -> Option<PathBuf> {
-        match &mut *builder.config.initial_rustfmt.borrow_mut() {
+    pub(crate) fn initial_rustfmt(&self) -> Option<PathBuf> {
+        match &mut *self.initial_rustfmt.borrow_mut() {
             RustfmtState::SystemToolchain(p) | RustfmtState::Downloaded(p) => Some(p.clone()),
             RustfmtState::Unavailable => None,
             r @ RustfmtState::LazyEvaluated => {
-                if builder.config.dry_run() {
+                if self.dry_run() {
                     return Some(PathBuf::new());
                 }
-                let path = maybe_download_rustfmt(builder);
+                let path = self.maybe_download_rustfmt();
                 *r = if let Some(p) = &path {
                     RustfmtState::Downloaded(p.clone())
                 } else {
@@ -1539,8 +1540,10 @@ impl Config {
         }
     }
 
-    pub fn verbose(&self) -> bool {
-        self.verbose > 0
+    pub fn verbose(&self, msg: &str) {
+        if self.verbose > 0 {
+            println!("{}", msg);
+        }
     }
 
     pub fn sanitizers_enabled(&self, target: TargetSelection) -> bool {
@@ -1578,218 +1581,77 @@ impl Config {
     pub fn submodules(&self, rust_info: &GitInfo) -> bool {
         self.submodules.unwrap_or(rust_info.is_managed_git_subrepository())
     }
-}
-
-fn set<T>(field: &mut T, val: Option<T>) {
-    if let Some(v) = val {
-        *field = v;
-    }
-}
 
-fn threads_from_config(v: u32) -> u32 {
-    match v {
-        0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
-        n => n,
-    }
-}
+    /// Returns the commit to download, or `None` if we shouldn't download CI artifacts.
+    fn download_ci_rustc_commit(&self, download_rustc: Option<StringOrBool>) -> Option<String> {
+        // If `download-rustc` is not set, default to rebuilding.
+        let if_unchanged = match download_rustc {
+            None | Some(StringOrBool::Bool(false)) => return None,
+            Some(StringOrBool::Bool(true)) => false,
+            Some(StringOrBool::String(s)) if s == "if-unchanged" => true,
+            Some(StringOrBool::String(other)) => {
+                panic!("unrecognized option for download-rustc: {}", other)
+            }
+        };
 
-/// Returns the commit to download, or `None` if we shouldn't download CI artifacts.
-fn download_ci_rustc_commit(
-    config: &Config,
-    download_rustc: Option<StringOrBool>,
-) -> Option<String> {
-    // If `download-rustc` is not set, default to rebuilding.
-    let if_unchanged = match download_rustc {
-        None | Some(StringOrBool::Bool(false)) => return None,
-        Some(StringOrBool::Bool(true)) => false,
-        Some(StringOrBool::String(s)) if s == "if-unchanged" => true,
-        Some(StringOrBool::String(other)) => {
-            panic!("unrecognized option for download-rustc: {}", other)
+        // Handle running from a directory other than the top level
+        let top_level = output(self.git().args(&["rev-parse", "--show-toplevel"]));
+        let top_level = top_level.trim_end();
+        let compiler = format!("{top_level}/compiler/");
+        let library = format!("{top_level}/library/");
+
+        // Look for a version to compare to based on the current commit.
+        // Only commits merged by bors will have CI artifacts.
+        let merge_base = output(
+            self.git()
+                .arg("rev-list")
+                .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email))
+                .args(&["-n1", "--first-parent", "HEAD"]),
+        );
+        let commit = merge_base.trim_end();
+        if commit.is_empty() {
+            println!("error: could not find commit hash for downloading rustc");
+            println!("help: maybe your repository history is too shallow?");
+            println!("help: consider disabling `download-rustc`");
+            println!("help: or fetch enough history to include one upstream commit");
+            crate::detail_exit(1);
         }
-    };
-
-    // Handle running from a directory other than the top level
-    let top_level = output(config.git().args(&["rev-parse", "--show-toplevel"]));
-    let top_level = top_level.trim_end();
-    let compiler = format!("{top_level}/compiler/");
-    let library = format!("{top_level}/library/");
 
-    // Look for a version to compare to based on the current commit.
-    // Only commits merged by bors will have CI artifacts.
-    let merge_base = output(
-        config
+        // Warn if there were changes to the compiler or standard library since the ancestor commit.
+        let has_changes = !t!(self
             .git()
-            .arg("rev-list")
-            .arg(format!("--author={}", config.stage0_metadata.config.git_merge_commit_email))
-            .args(&["-n1", "--first-parent", "HEAD"]),
-    );
-    let commit = merge_base.trim_end();
-    if commit.is_empty() {
-        println!("error: could not find commit hash for downloading rustc");
-        println!("help: maybe your repository history is too shallow?");
-        println!("help: consider disabling `download-rustc`");
-        println!("help: or fetch enough history to include one upstream commit");
-        crate::detail_exit(1);
-    }
-
-    // Warn if there were changes to the compiler or standard library since the ancestor commit.
-    let has_changes = !t!(config
-        .git()
-        .args(&["diff-index", "--quiet", &commit, "--", &compiler, &library])
-        .status())
-    .success();
-    if has_changes {
-        if if_unchanged {
-            if config.verbose > 0 {
-                println!(
-                    "warning: saw changes to compiler/ or library/ since {commit}; \
-                          ignoring `download-rustc`"
-                );
+            .args(&["diff-index", "--quiet", &commit, "--", &compiler, &library])
+            .status())
+        .success();
+        if has_changes {
+            if if_unchanged {
+                if self.verbose > 0 {
+                    println!(
+                        "warning: saw changes to compiler/ or library/ since {commit}; \
+                            ignoring `download-rustc`"
+                    );
+                }
+                return None;
             }
-            return None;
+            println!(
+                "warning: `download-rustc` is enabled, but there are changes to \
+                    compiler/ or library/"
+            );
         }
-        println!(
-            "warning: `download-rustc` is enabled, but there are changes to \
-                  compiler/ or library/"
-        );
-    }
-
-    Some(commit.to_string())
-}
-
-fn maybe_download_rustfmt(builder: &Builder<'_>) -> Option<PathBuf> {
-    let RustfmtMetadata { date, version } = builder.config.stage0_metadata.rustfmt.as_ref()?;
-    let channel = format!("{version}-{date}");
 
-    let host = builder.config.build;
-    let rustfmt_path = builder.config.initial_rustc.with_file_name(exe("rustfmt", host));
-    let bin_root = builder.config.out.join(host.triple).join("stage0");
-    let rustfmt_stamp = bin_root.join(".rustfmt-stamp");
-    if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) {
-        return Some(rustfmt_path);
+        Some(commit.to_string())
     }
-
-    let filename = format!("rustfmt-{version}-{build}.tar.xz", build = host.triple);
-    download_component(builder, DownloadSource::Dist, filename, "rustfmt-preview", &date, "stage0");
-
-    builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt"));
-    builder.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt"));
-
-    builder.create(&rustfmt_stamp, &channel);
-    Some(rustfmt_path)
 }
 
-fn download_ci_rustc(builder: &Builder<'_>, commit: &str) {
-    builder.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})"));
-    let version = builder.config.artifact_version_part(builder, commit);
-    let host = builder.config.build.triple;
-    let bin_root = builder.out.join(host).join("ci-rustc");
-    let rustc_stamp = bin_root.join(".rustc-stamp");
-
-    if !bin_root.join("bin").join("rustc").exists() || program_out_of_date(&rustc_stamp, commit) {
-        if bin_root.exists() {
-            t!(fs::remove_dir_all(&bin_root));
-        }
-        let filename = format!("rust-std-{version}-{host}.tar.xz");
-        let pattern = format!("rust-std-{host}");
-        download_ci_component(builder, filename, &pattern, commit);
-        let filename = format!("rustc-{version}-{host}.tar.xz");
-        download_ci_component(builder, filename, "rustc", commit);
-        // download-rustc doesn't need its own cargo, it can just use beta's.
-        let filename = format!("rustc-dev-{version}-{host}.tar.xz");
-        download_ci_component(builder, filename, "rustc-dev", commit);
-
-        builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustc"));
-        builder.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc"));
-        let lib_dir = bin_root.join("lib");
-        for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) {
-            let lib = t!(lib);
-            if lib.path().extension() == Some(OsStr::new("so")) {
-                builder.fix_bin_or_dylib(&lib.path());
-            }
-        }
-        t!(fs::write(rustc_stamp, commit));
+fn set<T>(field: &mut T, val: Option<T>) {
+    if let Some(v) = val {
+        *field = v;
     }
 }
 
-pub(crate) enum DownloadSource {
-    CI,
-    Dist,
-}
-
-/// Download a single component of a CI-built toolchain (not necessarily a published nightly).
-// NOTE: intentionally takes an owned string to avoid downloading multiple times by accident
-fn download_ci_component(builder: &Builder<'_>, filename: String, prefix: &str, commit: &str) {
-    download_component(builder, DownloadSource::CI, filename, prefix, commit, "ci-rustc")
-}
-
-fn download_component(
-    builder: &Builder<'_>,
-    mode: DownloadSource,
-    filename: String,
-    prefix: &str,
-    key: &str,
-    destination: &str,
-) {
-    let cache_dst = builder.out.join("cache");
-    let cache_dir = cache_dst.join(key);
-    if !cache_dir.exists() {
-        t!(fs::create_dir_all(&cache_dir));
-    }
-
-    let bin_root = builder.out.join(builder.config.build.triple).join(destination);
-    let tarball = cache_dir.join(&filename);
-    let (base_url, url, should_verify) = match mode {
-        DownloadSource::CI => (
-            builder.config.stage0_metadata.config.artifacts_server.clone(),
-            format!("{key}/{filename}"),
-            false,
-        ),
-        DownloadSource::Dist => {
-            let dist_server = env::var("RUSTUP_DIST_SERVER")
-                .unwrap_or(builder.config.stage0_metadata.config.dist_server.to_string());
-            // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0.json
-            (dist_server, format!("dist/{key}/{filename}"), true)
-        }
-    };
-
-    // For the beta compiler, put special effort into ensuring the checksums are valid.
-    // FIXME: maybe we should do this for download-rustc as well? but it would be a pain to update
-    // this on each and every nightly ...
-    let checksum = if should_verify {
-        let error = format!(
-            "src/stage0.json doesn't contain a checksum for {url}. \
-            Pre-built artifacts might not be available for this \
-            target at this time, see https://doc.rust-lang.org/nightly\
-            /rustc/platform-support.html for more information."
-        );
-        let sha256 = builder.config.stage0_metadata.checksums_sha256.get(&url).expect(&error);
-        if tarball.exists() {
-            if builder.verify(&tarball, sha256) {
-                builder.unpack(&tarball, &bin_root, prefix);
-                return;
-            } else {
-                builder.verbose(&format!(
-                    "ignoring cached file {} due to failed verification",
-                    tarball.display()
-                ));
-                builder.remove(&tarball);
-            }
-        }
-        Some(sha256)
-    } else if tarball.exists() {
-        builder.unpack(&tarball, &bin_root, prefix);
-        return;
-    } else {
-        None
-    };
-
-    builder.download_component(&format!("{base_url}/{url}"), &tarball, "");
-    if let Some(sha256) = checksum {
-        if !builder.verify(&tarball, sha256) {
-            panic!("failed to verify {}", tarball.display());
-        }
+fn threads_from_config(v: u32) -> u32 {
+    match v {
+        0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32,
+        n => n,
     }
-
-    builder.unpack(&tarball, &bin_root, prefix);
 }
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 9fbe476534e..aacd2c7eab9 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -924,13 +924,13 @@ impl Step for PlainSourceTarball {
 
         // Create the version file
         builder.create(&plain_dst_src.join("version"), &builder.rust_version());
-        if let Some(info) = builder.rust_info.info() {
+        if let Some(info) = builder.rust_info().info() {
             channel::write_commit_hash_file(&plain_dst_src, &info.sha);
             channel::write_commit_info_file(&plain_dst_src, info);
         }
 
         // If we're building from git sources, we need to vendor a complete distribution.
-        if builder.rust_info.is_managed_git_subrepository() {
+        if builder.rust_info().is_managed_git_subrepository() {
             // Ensure we have the submodules checked out.
             builder.update_submodule(Path::new("src/tools/rust-analyzer"));
 
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index c7d21bf3cdb..3180a12c85b 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -405,8 +405,8 @@ impl Step for SharedAssets {
         if !builder.config.dry_run() && !up_to_date(&version_input, &version_info) {
             let info = t!(fs::read_to_string(&version_input))
                 .replace("VERSION", &builder.rust_release())
-                .replace("SHORT_HASH", builder.rust_info.sha_short().unwrap_or(""))
-                .replace("STAMP", builder.rust_info.sha().unwrap_or(""));
+                .replace("SHORT_HASH", builder.rust_info().sha_short().unwrap_or(""))
+                .replace("STAMP", builder.rust_info().sha().unwrap_or(""));
             t!(fs::write(&version_info, &info));
         }
 
@@ -965,7 +965,7 @@ impl Step for RustcBook {
         cmd.arg("--rustc");
         cmd.arg(&rustc);
         cmd.arg("--rustc-target").arg(&self.target.rustc_target_arg());
-        if builder.config.verbose() {
+        if builder.is_verbose() {
             cmd.arg("--verbose");
         }
         if self.validate {
diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs
new file mode 100644
index 00000000000..d0f389df973
--- /dev/null
+++ b/src/bootstrap/download.rs
@@ -0,0 +1,519 @@
+use std::{
+    env,
+    ffi::{OsStr, OsString},
+    fs::{self, File},
+    io::{BufRead, BufReader, ErrorKind},
+    path::{Path, PathBuf},
+    process::{Command, Stdio},
+};
+
+use once_cell::sync::OnceCell;
+use xz2::bufread::XzDecoder;
+
+use crate::{
+    config::RustfmtMetadata,
+    native::detect_llvm_sha,
+    t,
+    util::{check_run, exe, program_out_of_date, try_run},
+    Config,
+};
+
+/// Generic helpers that are useful anywhere in bootstrap.
+impl Config {
+    pub fn is_verbose(&self) -> bool {
+        self.verbose > 0
+    }
+
+    pub(crate) fn create(&self, path: &Path, s: &str) {
+        if self.dry_run() {
+            return;
+        }
+        t!(fs::write(path, s));
+    }
+
+    pub(crate) fn remove(&self, f: &Path) {
+        if self.dry_run() {
+            return;
+        }
+        fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f));
+    }
+
+    /// Create a temporary directory in `out` and return its path.
+    ///
+    /// NOTE: this temporary directory is shared between all steps;
+    /// if you need an empty directory, create a new subdirectory inside it.
+    pub(crate) fn tempdir(&self) -> PathBuf {
+        let tmp = self.out.join("tmp");
+        t!(fs::create_dir_all(&tmp));
+        tmp
+    }
+
+    /// Runs a command, printing out nice contextual information if it fails.
+    /// Exits if the command failed to execute at all, otherwise returns its
+    /// `status.success()`.
+    pub(crate) fn try_run(&self, cmd: &mut Command) -> bool {
+        if self.dry_run() {
+            return true;
+        }
+        self.verbose(&format!("running: {:?}", cmd));
+        try_run(cmd, self.is_verbose())
+    }
+
+    /// Runs a command, printing out nice contextual information if it fails.
+    /// Returns false if do not execute at all, otherwise returns its
+    /// `status.success()`.
+    pub(crate) fn check_run(&self, cmd: &mut Command) -> bool {
+        if self.dry_run() {
+            return true;
+        }
+        self.verbose(&format!("running: {:?}", cmd));
+        check_run(cmd, self.is_verbose())
+    }
+
+    /// Modifies the interpreter section of 'fname' to fix the dynamic linker,
+    /// or the RPATH section, to fix the dynamic library search path
+    ///
+    /// This is only required on NixOS and uses the PatchELF utility to
+    /// change the interpreter/RPATH of ELF executables.
+    ///
+    /// Please see https://nixos.org/patchelf.html for more information
+    fn fix_bin_or_dylib(&self, fname: &Path) {
+        // FIXME: cache NixOS detection?
+        match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
+            Err(_) => return,
+            Ok(output) if !output.status.success() => return,
+            Ok(output) => {
+                let mut s = output.stdout;
+                if s.last() == Some(&b'\n') {
+                    s.pop();
+                }
+                if s != b"Linux" {
+                    return;
+                }
+            }
+        }
+
+        // If the user has asked binaries to be patched for Nix, then
+        // don't check for NixOS or `/lib`, just continue to the patching.
+        // NOTE: this intentionally comes after the Linux check:
+        // - patchelf only works with ELF files, so no need to run it on Mac or Windows
+        // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc.
+        if !self.patch_binaries_for_nix {
+            // Use `/etc/os-release` instead of `/etc/NIXOS`.
+            // The latter one does not exist on NixOS when using tmpfs as root.
+            const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""];
+            let os_release = match File::open("/etc/os-release") {
+                Err(e) if e.kind() == ErrorKind::NotFound => return,
+                Err(e) => panic!("failed to access /etc/os-release: {}", e),
+                Ok(f) => f,
+            };
+            if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) {
+                return;
+            }
+            if Path::new("/lib").exists() {
+                return;
+            }
+        }
+
+        // At this point we're pretty sure the user is running NixOS or using Nix
+        println!("info: you seem to be using Nix. Attempting to patch {}", fname.display());
+
+        // Only build `.nix-deps` once.
+        static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
+        let mut nix_build_succeeded = true;
+        let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
+            // Run `nix-build` to "build" each dependency (which will likely reuse
+            // the existing `/nix/store` copy, or at most download a pre-built copy).
+            //
+            // Importantly, we create a gc-root called `.nix-deps` in the `build/`
+            // directory, but still reference the actual `/nix/store` path in the rpath
+            // as it makes it significantly more robust against changes to the location of
+            // the `.nix-deps` location.
+            //
+            // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
+            // zlib: Needed as a system dependency of `libLLVM-*.so`.
+            // patchelf: Needed for patching ELF binaries (see doc comment above).
+            let nix_deps_dir = self.out.join(".nix-deps");
+            const NIX_EXPR: &str = "
+            with (import <nixpkgs> {});
+            symlinkJoin {
+                name = \"rust-stage0-dependencies\";
+                paths = [
+                    zlib
+                    patchelf
+                    stdenv.cc.bintools
+                ];
+            }
+            ";
+            nix_build_succeeded = self.try_run(Command::new("nix-build").args(&[
+                Path::new("-E"),
+                Path::new(NIX_EXPR),
+                Path::new("-o"),
+                &nix_deps_dir,
+            ]));
+            nix_deps_dir
+        });
+        if !nix_build_succeeded {
+            return;
+        }
+
+        let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
+        let rpath_entries = {
+            // ORIGIN is a relative default, all binary and dynamic libraries we ship
+            // appear to have this (even when `../lib` is redundant).
+            // NOTE: there are only two paths here, delimited by a `:`
+            let mut entries = OsString::from("$ORIGIN/../lib:");
+            entries.push(t!(fs::canonicalize(nix_deps_dir)));
+            entries.push("/lib");
+            entries
+        };
+        patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
+        if !fname.extension().map_or(false, |ext| ext == "so") {
+            // Finally, set the correct .interp for binaries
+            let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
+            // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
+            let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
+            patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]);
+        }
+
+        self.try_run(patchelf.arg(fname));
+    }
+
+    fn download_file(&self, url: &str, dest_path: &Path, help_on_error: &str) {
+        self.verbose(&format!("download {url}"));
+        // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
+        let tempfile = self.tempdir().join(dest_path.file_name().unwrap());
+        // While bootstrap itself only supports http and https downloads, downstream forks might
+        // need to download components from other protocols. The match allows them adding more
+        // protocols without worrying about merge conflicts if we change the HTTP implementation.
+        match url.split_once("://").map(|(proto, _)| proto) {
+            Some("http") | Some("https") => {
+                self.download_http_with_retries(&tempfile, url, help_on_error)
+            }
+            Some(other) => panic!("unsupported protocol {other} in {url}"),
+            None => panic!("no protocol in {url}"),
+        }
+        t!(std::fs::rename(&tempfile, dest_path));
+    }
+
+    fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
+        println!("downloading {}", url);
+        // Try curl. If that fails and we are on windows, fallback to PowerShell.
+        let mut curl = Command::new("curl");
+        curl.args(&[
+            "-#",
+            "-y",
+            "30",
+            "-Y",
+            "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
+            "--connect-timeout",
+            "30", // timeout if cannot connect within 30 seconds
+            "--retry",
+            "3",
+            "-Sf",
+            "-o",
+        ]);
+        curl.arg(tempfile);
+        curl.arg(url);
+        if !self.check_run(&mut curl) {
+            if self.build.contains("windows-msvc") {
+                println!("Fallback to PowerShell");
+                for _ in 0..3 {
+                    if self.try_run(Command::new("PowerShell.exe").args(&[
+                        "/nologo",
+                        "-Command",
+                        "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
+                        &format!(
+                            "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
+                            url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"),
+                        ),
+                    ])) {
+                        return;
+                    }
+                    println!("\nspurious failure, trying again");
+                }
+            }
+            if !help_on_error.is_empty() {
+                eprintln!("{}", help_on_error);
+            }
+            crate::detail_exit(1);
+        }
+    }
+
+    fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) {
+        println!("extracting {} to {}", tarball.display(), dst.display());
+        if !dst.exists() {
+            t!(fs::create_dir_all(dst));
+        }
+
+        // `tarball` ends with `.tar.xz`; strip that suffix
+        // example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
+        let uncompressed_filename =
+            Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
+        let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());
+
+        // decompress the file
+        let data = t!(File::open(tarball));
+        let decompressor = XzDecoder::new(BufReader::new(data));
+
+        let mut tar = tar::Archive::new(decompressor);
+        for member in t!(tar.entries()) {
+            let mut member = t!(member);
+            let original_path = t!(member.path()).into_owned();
+            // skip the top-level directory
+            if original_path == directory_prefix {
+                continue;
+            }
+            let mut short_path = t!(original_path.strip_prefix(directory_prefix));
+            if !short_path.starts_with(pattern) {
+                continue;
+            }
+            short_path = t!(short_path.strip_prefix(pattern));
+            let dst_path = dst.join(short_path);
+            self.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
+            if !t!(member.unpack_in(dst)) {
+                panic!("path traversal attack ??");
+            }
+            let src_path = dst.join(original_path);
+            if src_path.is_dir() && dst_path.exists() {
+                continue;
+            }
+            t!(fs::rename(src_path, dst_path));
+        }
+        t!(fs::remove_dir_all(dst.join(directory_prefix)));
+    }
+
+    /// Returns whether the SHA256 checksum of `path` matches `expected`.
+    fn verify(&self, path: &Path, expected: &str) -> bool {
+        use sha2::Digest;
+
+        self.verbose(&format!("verifying {}", path.display()));
+        let mut hasher = sha2::Sha256::new();
+        // FIXME: this is ok for rustfmt (4.1 MB large at time of writing), but it seems memory-intensive for rustc and larger components.
+        // Consider using streaming IO instead?
+        let contents = if self.dry_run() { vec![] } else { t!(fs::read(path)) };
+        hasher.update(&contents);
+        let found = hex::encode(hasher.finalize().as_slice());
+        let verified = found == expected;
+        if !verified && !self.dry_run() {
+            println!(
+                "invalid checksum: \n\
+                found:    {found}\n\
+                expected: {expected}",
+            );
+        }
+        return verified;
+    }
+}
+
+enum DownloadSource {
+    CI,
+    Dist,
+}
+
+/// Functions that are only ever called once, but named for clarify and to avoid thousand-line functions.
+impl Config {
+    pub(crate) fn maybe_download_rustfmt(&self) -> Option<PathBuf> {
+        let RustfmtMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?;
+        let channel = format!("{version}-{date}");
+
+        let host = self.build;
+        let rustfmt_path = self.initial_rustc.with_file_name(exe("rustfmt", host));
+        let bin_root = self.out.join(host.triple).join("stage0");
+        let rustfmt_stamp = bin_root.join(".rustfmt-stamp");
+        if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) {
+            return Some(rustfmt_path);
+        }
+
+        let filename = format!("rustfmt-{version}-{build}.tar.xz", build = host.triple);
+        self.download_component(DownloadSource::Dist, filename, "rustfmt-preview", &date, "stage0");
+
+        self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt"));
+        self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt"));
+
+        self.create(&rustfmt_stamp, &channel);
+        Some(rustfmt_path)
+    }
+
+    pub(crate) fn download_ci_rustc(&self, commit: &str) {
+        self.verbose(&format!("using downloaded stage2 artifacts from CI (commit {commit})"));
+        let version = self.artifact_version_part(commit);
+        let host = self.build.triple;
+        let bin_root = self.out.join(host).join("ci-rustc");
+        let rustc_stamp = bin_root.join(".rustc-stamp");
+
+        if !bin_root.join("bin").join("rustc").exists() || program_out_of_date(&rustc_stamp, commit)
+        {
+            if bin_root.exists() {
+                t!(fs::remove_dir_all(&bin_root));
+            }
+            let filename = format!("rust-std-{version}-{host}.tar.xz");
+            let pattern = format!("rust-std-{host}");
+            self.download_ci_component(filename, &pattern, commit);
+            let filename = format!("rustc-{version}-{host}.tar.xz");
+            self.download_ci_component(filename, "rustc", commit);
+            // download-rustc doesn't need its own cargo, it can just use beta's.
+            let filename = format!("rustc-dev-{version}-{host}.tar.xz");
+            self.download_ci_component(filename, "rustc-dev", commit);
+            let filename = format!("rust-src-{version}.tar.xz");
+            self.download_ci_component(filename, "rust-src", commit);
+
+            self.fix_bin_or_dylib(&bin_root.join("bin").join("rustc"));
+            self.fix_bin_or_dylib(&bin_root.join("bin").join("rustdoc"));
+            let lib_dir = bin_root.join("lib");
+            for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) {
+                let lib = t!(lib);
+                if lib.path().extension() == Some(OsStr::new("so")) {
+                    self.fix_bin_or_dylib(&lib.path());
+                }
+            }
+            t!(fs::write(rustc_stamp, commit));
+        }
+    }
+
+    /// Download a single component of a CI-built toolchain (not necessarily a published nightly).
+    // NOTE: intentionally takes an owned string to avoid downloading multiple times by accident
+    fn download_ci_component(&self, filename: String, prefix: &str, commit: &str) {
+        Self::download_component(self, DownloadSource::CI, filename, prefix, commit, "ci-rustc")
+    }
+
+    fn download_component(
+        &self,
+        mode: DownloadSource,
+        filename: String,
+        prefix: &str,
+        key: &str,
+        destination: &str,
+    ) {
+        let cache_dst = self.out.join("cache");
+        let cache_dir = cache_dst.join(key);
+        if !cache_dir.exists() {
+            t!(fs::create_dir_all(&cache_dir));
+        }
+
+        let bin_root = self.out.join(self.build.triple).join(destination);
+        let tarball = cache_dir.join(&filename);
+        let (base_url, url, should_verify) = match mode {
+            DownloadSource::CI => (
+                self.stage0_metadata.config.artifacts_server.clone(),
+                format!("{key}/{filename}"),
+                false,
+            ),
+            DownloadSource::Dist => {
+                let dist_server = env::var("RUSTUP_DIST_SERVER")
+                    .unwrap_or(self.stage0_metadata.config.dist_server.to_string());
+                // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0.json
+                (dist_server, format!("dist/{key}/{filename}"), true)
+            }
+        };
+
+        // For the beta compiler, put special effort into ensuring the checksums are valid.
+        // FIXME: maybe we should do this for download-rustc as well? but it would be a pain to update
+        // this on each and every nightly ...
+        let checksum = if should_verify {
+            let error = format!(
+                "src/stage0.json doesn't contain a checksum for {url}. \
+                Pre-built artifacts might not be available for this \
+                target at this time, see https://doc.rust-lang.org/nightly\
+                /rustc/platform-support.html for more information."
+            );
+            let sha256 = self.stage0_metadata.checksums_sha256.get(&url).expect(&error);
+            if tarball.exists() {
+                if self.verify(&tarball, sha256) {
+                    self.unpack(&tarball, &bin_root, prefix);
+                    return;
+                } else {
+                    self.verbose(&format!(
+                        "ignoring cached file {} due to failed verification",
+                        tarball.display()
+                    ));
+                    self.remove(&tarball);
+                }
+            }
+            Some(sha256)
+        } else if tarball.exists() {
+            self.unpack(&tarball, &bin_root, prefix);
+            return;
+        } else {
+            None
+        };
+
+        self.download_file(&format!("{base_url}/{url}"), &tarball, "");
+        if let Some(sha256) = checksum {
+            if !self.verify(&tarball, sha256) {
+                panic!("failed to verify {}", tarball.display());
+            }
+        }
+
+        self.unpack(&tarball, &bin_root, prefix);
+    }
+
+    pub(crate) fn maybe_download_ci_llvm(&self) {
+        if !self.llvm_from_ci {
+            return;
+        }
+        let llvm_root = self.ci_llvm_root();
+        let llvm_stamp = llvm_root.join(".llvm-stamp");
+        let llvm_sha = detect_llvm_sha(&self, self.rust_info.is_managed_git_subrepository());
+        let key = format!("{}{}", llvm_sha, self.llvm_assertions);
+        if program_out_of_date(&llvm_stamp, &key) && !self.dry_run() {
+            self.download_ci_llvm(&llvm_sha);
+            for entry in t!(fs::read_dir(llvm_root.join("bin"))) {
+                self.fix_bin_or_dylib(&t!(entry).path());
+            }
+
+            // Update the timestamp of llvm-config to force rustc_llvm to be
+            // rebuilt. This is a hacky workaround for a deficiency in Cargo where
+            // the rerun-if-changed directive doesn't handle changes very well.
+            // https://github.com/rust-lang/cargo/issues/10791
+            // Cargo only compares the timestamp of the file relative to the last
+            // time `rustc_llvm` build script ran. However, the timestamps of the
+            // files in the tarball are in the past, so it doesn't trigger a
+            // rebuild.
+            let now = filetime::FileTime::from_system_time(std::time::SystemTime::now());
+            let llvm_config = llvm_root.join("bin").join(exe("llvm-config", self.build));
+            t!(filetime::set_file_times(&llvm_config, now, now));
+
+            let llvm_lib = llvm_root.join("lib");
+            for entry in t!(fs::read_dir(&llvm_lib)) {
+                let lib = t!(entry).path();
+                if lib.extension().map_or(false, |ext| ext == "so") {
+                    self.fix_bin_or_dylib(&lib);
+                }
+            }
+            t!(fs::write(llvm_stamp, key));
+        }
+    }
+
+    fn download_ci_llvm(&self, llvm_sha: &str) {
+        let llvm_assertions = self.llvm_assertions;
+
+        let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions);
+        let cache_dst = self.out.join("cache");
+        let rustc_cache = cache_dst.join(cache_prefix);
+        if !rustc_cache.exists() {
+            t!(fs::create_dir_all(&rustc_cache));
+        }
+        let base = if llvm_assertions {
+            &self.stage0_metadata.config.artifacts_with_llvm_assertions_server
+        } else {
+            &self.stage0_metadata.config.artifacts_server
+        };
+        let version = self.artifact_version_part(llvm_sha);
+        let filename = format!("rust-dev-{}-{}.tar.xz", version, self.build.triple);
+        let tarball = rustc_cache.join(&filename);
+        if !tarball.exists() {
+            let help_on_error = "error: failed to download llvm from ci
+
+    help: old builds get deleted after a certain time
+    help: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml:
+
+    [llvm]
+    download-ci-llvm = false
+    ";
+            self.download_file(&format!("{base}/{llvm_sha}/{filename}"), &tarball, help_on_error);
+        }
+        let llvm_root = self.ci_llvm_root();
+        self.unpack(&tarball, &llvm_root, "rust-dev");
+    }
+}
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index bbb5a18ba07..f4fa556b974 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -112,15 +112,14 @@ use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::str;
 
+use channel::GitInfo;
 use config::{DryRun, Target};
 use filetime::FileTime;
 use once_cell::sync::OnceCell;
 
 use crate::builder::Kind;
 use crate::config::{LlvmLibunwind, TargetSelection};
-use crate::util::{
-    check_run, exe, libdir, mtime, output, run, run_suppressed, try_run, try_run_suppressed, CiEnv,
-};
+use crate::util::{exe, libdir, mtime, output, run, run_suppressed, try_run_suppressed, CiEnv};
 
 mod bolt;
 mod builder;
@@ -133,6 +132,7 @@ mod compile;
 mod config;
 mod dist;
 mod doc;
+mod download;
 mod flags;
 mod format;
 mod install;
@@ -281,7 +281,6 @@ pub struct Build {
     src: PathBuf,
     out: PathBuf,
     bootstrap_out: PathBuf,
-    rust_info: channel::GitInfo,
     cargo_info: channel::GitInfo,
     rust_analyzer_info: channel::GitInfo,
     clippy_info: channel::GitInfo,
@@ -396,6 +395,28 @@ pub enum CLang {
     Cxx,
 }
 
+macro_rules! forward {
+    ( $( $fn:ident( $($param:ident: $ty:ty),* ) $( -> $ret:ty)? ),+ $(,)? ) => {
+        impl Build {
+            $( fn $fn(&self, $($param: $ty),* ) $( -> $ret)? {
+                self.config.$fn( $($param),* )
+            } )+
+        }
+    }
+}
+
+forward! {
+    verbose(msg: &str),
+    is_verbose() -> bool,
+    create(path: &Path, s: &str),
+    remove(f: &Path),
+    tempdir() -> PathBuf,
+    try_run(cmd: &mut Command) -> bool,
+    llvm_link_shared() -> bool,
+    download_rustc() -> bool,
+    initial_rustfmt() -> Option<PathBuf>,
+}
+
 impl Build {
     /// Creates a new set of build configuration from the `flags` on the command
     /// line and the filesystem `config`.
@@ -499,7 +520,6 @@ impl Build {
             out,
             bootstrap_out,
 
-            rust_info,
             cargo_info,
             rust_analyzer_info,
             clippy_info,
@@ -570,7 +590,7 @@ impl Build {
             t!(std::fs::read_dir(dir)).next().is_none()
         }
 
-        if !self.config.submodules(&self.rust_info) {
+        if !self.config.submodules(&self.rust_info()) {
             return;
         }
 
@@ -636,7 +656,7 @@ impl Build {
     /// This avoids contributors checking in a submodule change by accident.
     pub fn maybe_update_submodules(&self) {
         // Avoid running git when there isn't a git checkout.
-        if !self.config.submodules(&self.rust_info) {
+        if !self.config.submodules(&self.rust_info()) {
             return;
         }
         let output = output(
@@ -735,6 +755,10 @@ impl Build {
         cleared
     }
 
+    fn rust_info(&self) -> &GitInfo {
+        &self.config.rust_info
+    }
+
     /// Gets the space-separated set of activated features for the standard
     /// library.
     fn std_features(&self, target: TargetSelection) -> String {
@@ -966,17 +990,6 @@ impl Build {
     /// Runs a command, printing out nice contextual information if it fails.
     /// Exits if the command failed to execute at all, otherwise returns its
     /// `status.success()`.
-    fn try_run(&self, cmd: &mut Command) -> bool {
-        if self.config.dry_run() {
-            return true;
-        }
-        self.verbose(&format!("running: {:?}", cmd));
-        try_run(cmd, self.is_verbose())
-    }
-
-    /// Runs a command, printing out nice contextual information if it fails.
-    /// Exits if the command failed to execute at all, otherwise returns its
-    /// `status.success()`.
     fn try_run_quiet(&self, cmd: &mut Command) -> bool {
         if self.config.dry_run() {
             return true;
@@ -985,28 +998,6 @@ impl Build {
         try_run_suppressed(cmd)
     }
 
-    /// Runs a command, printing out nice contextual information if it fails.
-    /// Returns false if do not execute at all, otherwise returns its
-    /// `status.success()`.
-    fn check_run(&self, cmd: &mut Command) -> bool {
-        if self.config.dry_run() {
-            return true;
-        }
-        self.verbose(&format!("running: {:?}", cmd));
-        check_run(cmd, self.is_verbose())
-    }
-
-    pub fn is_verbose(&self) -> bool {
-        self.verbosity > 0
-    }
-
-    /// Prints a message if this build is configured in verbose mode.
-    fn verbose(&self, msg: &str) {
-        if self.is_verbose() {
-            println!("{}", msg);
-        }
-    }
-
     pub fn is_verbose_than(&self, level: usize) -> bool {
         self.verbosity > level
     }
@@ -1269,7 +1260,7 @@ impl Build {
         match &self.config.channel[..] {
             "stable" => num.to_string(),
             "beta" => {
-                if self.rust_info.is_managed_git_subrepository() && !self.config.ignore_git {
+                if self.rust_info().is_managed_git_subrepository() && !self.config.ignore_git {
                     format!("{}-beta.{}", num, self.beta_prerelease_version())
                 } else {
                     format!("{}-beta", num)
@@ -1329,7 +1320,7 @@ impl Build {
     /// Note that this is a descriptive string which includes the commit date,
     /// sha, version, etc.
     fn rust_version(&self) -> String {
-        let mut version = self.rust_info.version(self, &self.version);
+        let mut version = self.rust_info().version(self, &self.version);
         if let Some(ref s) = self.config.description {
             version.push_str(" (");
             version.push_str(s);
@@ -1340,7 +1331,7 @@ impl Build {
 
     /// Returns the full commit hash.
     fn rust_sha(&self) -> Option<&str> {
-        self.rust_info.sha()
+        self.rust_info().sha()
     }
 
     /// Returns the `a.b.c` version that the given package is at.
@@ -1426,16 +1417,6 @@ impl Build {
         paths
     }
 
-    /// Create a temporary directory in `out` and return its path.
-    ///
-    /// NOTE: this temporary directory is shared between all steps;
-    /// if you need an empty directory, create a new subdirectory inside it.
-    fn tempdir(&self) -> PathBuf {
-        let tmp = self.out.join("tmp");
-        t!(fs::create_dir_all(&tmp));
-        tmp
-    }
-
     /// Copies a file from `src` to `dst`
     pub fn copy(&self, src: &Path, dst: &Path) {
         self.copy_internal(src, dst, false);
@@ -1545,13 +1526,6 @@ impl Build {
         chmod(&dst, perms);
     }
 
-    fn create(&self, path: &Path, s: &str) {
-        if self.config.dry_run() {
-            return;
-        }
-        t!(fs::write(path, s));
-    }
-
     fn read(&self, path: &Path) -> String {
         if self.config.dry_run() {
             return String::new();
@@ -1590,13 +1564,6 @@ impl Build {
         if !self.config.dry_run() { symlink_file(src.as_ref(), link.as_ref()) } else { Ok(()) }
     }
 
-    fn remove(&self, f: &Path) {
-        if self.config.dry_run() {
-            return;
-        }
-        fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {:?}", f));
-    }
-
     /// Returns if config.ninja is enabled, and checks for ninja existence,
     /// exiting with a nicer error message if not.
     fn ninja(&self) -> bool {
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 2407291ceea..f6c453ebe10 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -19,9 +19,9 @@ use std::process::Command;
 use crate::bolt::{instrument_with_bolt_inplace, optimize_library_with_bolt_inplace};
 use crate::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::channel;
-use crate::config::TargetSelection;
+use crate::config::{Config, TargetSelection};
 use crate::util::get_clang_cl_resource_dir;
-use crate::util::{self, exe, output, program_out_of_date, t, up_to_date};
+use crate::util::{self, exe, output, t, up_to_date};
 use crate::{CLang, GitRepo};
 
 pub struct Meta {
@@ -65,7 +65,7 @@ pub fn prebuilt_llvm_config(
     builder: &Builder<'_>,
     target: TargetSelection,
 ) -> Result<PathBuf, Meta> {
-    maybe_download_ci_llvm(builder);
+    builder.config.maybe_download_ci_llvm();
 
     // If we're using a custom LLVM bail out here, but we can only use a
     // custom LLVM for the build triple.
@@ -117,7 +117,7 @@ pub fn prebuilt_llvm_config(
 }
 
 /// This retrieves the LLVM sha we *want* to use, according to git history.
-pub(crate) fn detect_llvm_sha(config: &crate::config::Config, is_git: bool) -> String {
+pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
     let llvm_sha = if is_git {
         let mut rev_list = config.git();
         rev_list.args(&[
@@ -155,7 +155,7 @@ pub(crate) fn detect_llvm_sha(config: &crate::config::Config, is_git: bool) -> S
 /// This checks both the build triple platform to confirm we're usable at all,
 /// and then verifies if the current HEAD matches the detected LLVM SHA head,
 /// in which case LLVM is indicated as not available.
-pub(crate) fn is_ci_llvm_available(config: &crate::config::Config, asserts: bool) -> bool {
+pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
     // This is currently all tier 1 targets and tier 2 targets with host tools
     // (since others may not have CI artifacts)
     // https://doc.rust-lang.org/rustc/platform-support.html#tier-1
@@ -217,80 +217,6 @@ pub(crate) fn is_ci_llvm_available(config: &crate::config::Config, asserts: bool
     true
 }
 
-pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) {
-    let config = &builder.config;
-    if !config.llvm_from_ci {
-        return;
-    }
-    let llvm_root = config.ci_llvm_root();
-    let llvm_stamp = llvm_root.join(".llvm-stamp");
-    let llvm_sha = detect_llvm_sha(&config, builder.rust_info.is_managed_git_subrepository());
-    let key = format!("{}{}", llvm_sha, config.llvm_assertions);
-    if program_out_of_date(&llvm_stamp, &key) && !config.dry_run() {
-        download_ci_llvm(builder, &llvm_sha);
-        for entry in t!(fs::read_dir(llvm_root.join("bin"))) {
-            builder.fix_bin_or_dylib(&t!(entry).path());
-        }
-
-        // Update the timestamp of llvm-config to force rustc_llvm to be
-        // rebuilt. This is a hacky workaround for a deficiency in Cargo where
-        // the rerun-if-changed directive doesn't handle changes very well.
-        // https://github.com/rust-lang/cargo/issues/10791
-        // Cargo only compares the timestamp of the file relative to the last
-        // time `rustc_llvm` build script ran. However, the timestamps of the
-        // files in the tarball are in the past, so it doesn't trigger a
-        // rebuild.
-        let now = filetime::FileTime::from_system_time(std::time::SystemTime::now());
-        let llvm_config = llvm_root.join("bin").join(exe("llvm-config", builder.config.build));
-        t!(filetime::set_file_times(&llvm_config, now, now));
-
-        let llvm_lib = llvm_root.join("lib");
-        for entry in t!(fs::read_dir(&llvm_lib)) {
-            let lib = t!(entry).path();
-            if lib.extension().map_or(false, |ext| ext == "so") {
-                builder.fix_bin_or_dylib(&lib);
-            }
-        }
-        t!(fs::write(llvm_stamp, key));
-    }
-}
-
-fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) {
-    let llvm_assertions = builder.config.llvm_assertions;
-
-    let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions);
-    let cache_dst = builder.out.join("cache");
-    let rustc_cache = cache_dst.join(cache_prefix);
-    if !rustc_cache.exists() {
-        t!(fs::create_dir_all(&rustc_cache));
-    }
-    let base = if llvm_assertions {
-        &builder.config.stage0_metadata.config.artifacts_with_llvm_assertions_server
-    } else {
-        &builder.config.stage0_metadata.config.artifacts_server
-    };
-    let version = builder.config.artifact_version_part(builder, llvm_sha);
-    let filename = format!("rust-dev-{}-{}.tar.xz", version, builder.build.build.triple);
-    let tarball = rustc_cache.join(&filename);
-    if !tarball.exists() {
-        let help_on_error = "error: failed to download llvm from ci
-
-help: old builds get deleted after a certain time
-help: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml:
-
-[llvm]
-download-ci-llvm = false
-";
-        builder.download_component(
-            &format!("{base}/{llvm_sha}/{filename}"),
-            &tarball,
-            help_on_error,
-        );
-    }
-    let llvm_root = builder.config.ci_llvm_root();
-    builder.unpack(&tarball, &llvm_root, "rust-dev");
-}
-
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct Llvm {
     pub target: TargetSelection,
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 35c66cfd95f..631d42acb93 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -74,7 +74,7 @@ pub fn check(build: &mut Build) {
     let mut cmd_finder = Finder::new();
     // If we've got a git directory we're gonna need git to update
     // submodules and learn about various other aspects.
-    if build.rust_info.is_managed_git_subrepository() {
+    if build.rust_info().is_managed_git_subrepository() {
         cmd_finder.must_have("git");
     }
 
diff --git a/src/bootstrap/tarball.rs b/src/bootstrap/tarball.rs
index 82b063583c9..fc850a22b2f 100644
--- a/src/bootstrap/tarball.rs
+++ b/src/bootstrap/tarball.rs
@@ -298,7 +298,7 @@ impl<'a> Tarball<'a> {
     fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTarball {
         t!(std::fs::create_dir_all(&self.overlay_dir));
         self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder));
-        if let Some(info) = self.builder.rust_info.info() {
+        if let Some(info) = self.builder.rust_info().info() {
             channel::write_commit_hash_file(&self.overlay_dir, &info.sha);
             channel::write_commit_info_file(&self.overlay_dir, info);
         }
diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs
index 8a2f46abbd2..58220783228 100644
--- a/src/bootstrap/util.rs
+++ b/src/bootstrap/util.rs
@@ -44,7 +44,13 @@ pub use t;
 /// Given an executable called `name`, return the filename for the
 /// executable for a particular target.
 pub fn exe(name: &str, target: TargetSelection) -> String {
-    if target.contains("windows") { format!("{}.exe", name) } else { name.to_string() }
+    if target.contains("windows") {
+        format!("{}.exe", name)
+    } else if target.contains("uefi") {
+        format!("{}.efi", name)
+    } else {
+        name.to_string()
+    }
 }
 
 /// Returns `true` if the file name given looks like a dynamic library.
diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile
index 7d77fdd30d1..43a449b3a19 100644
--- a/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-s390x-linux/Dockerfile
@@ -28,5 +28,5 @@ ENV \
 
 ENV HOSTS=s390x-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --disable-docs
+ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-profiler --disable-docs
 ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile
index fed4be4c30a..d03c364547e 100644
--- a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/Dockerfile
@@ -1,7 +1,8 @@
-FROM ubuntu:16.04
+FROM ubuntu:20.04
 
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
+RUN DEBIAN_FRONTEND=noninteractive apt-get install -y zlib1g-dev
 
 COPY host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh /tmp/
 RUN /tmp/build-netbsd-toolchain.sh
@@ -9,9 +10,6 @@ RUN /tmp/build-netbsd-toolchain.sh
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
-COPY scripts/cmake.sh /scripts/
-RUN /scripts/cmake.sh
-
 ENV PATH=$PATH:/x-tools/x86_64-unknown-netbsd/bin
 
 ENV \
@@ -21,6 +19,5 @@ ENV \
 
 ENV HOSTS=x86_64-unknown-netbsd
 
-ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs \
-    --set llvm.allow-old-toolchain
+ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
 ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh
index 5dfa47b4eed..e0c008b76fa 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh
@@ -25,19 +25,19 @@ cd netbsd
 
 mkdir -p /x-tools/x86_64-unknown-netbsd/sysroot
 
-URL=https://ci-mirrors.rust-lang.org/rustc
-
-# Originally from ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-$BSD/source/sets/*.tgz
-curl $URL/2018-03-01-netbsd-src.tgz | tar xzf -
-curl $URL/2018-03-01-netbsd-gnusrc.tgz | tar xzf -
-curl $URL/2018-03-01-netbsd-sharesrc.tgz | tar xzf -
-curl $URL/2018-03-01-netbsd-syssrc.tgz | tar xzf -
-
-# Originally from ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-$BSD/amd64/binary/sets/*.tgz
-curl $URL/2018-03-01-netbsd-base.tgz | \
-  tar xzf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib ./lib
-curl $URL/2018-03-01-netbsd-comp.tgz | \
-  tar xzf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib
+# URL=https://ci-mirrors.rust-lang.org/rustc
+
+SOURCE_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/source/sets
+curl $SOURCE_URL/src.tgz | tar xzf -
+curl $SOURCE_URL/gnusrc.tgz | tar xzf -
+curl $SOURCE_URL/sharesrc.tgz | tar xzf -
+curl $SOURCE_URL/syssrc.tgz | tar xzf -
+
+BINARY_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/amd64/binary/sets
+curl $BINARY_URL/base.tar.xz | \
+  tar xJf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib ./lib
+curl $BINARY_URL/comp.tar.xz | \
+  tar xJf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib
 
 cd usr/src
 
diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py
new file mode 100644
index 00000000000..a2708d16947
--- /dev/null
+++ b/src/ci/docker/scripts/fuchsia-test-runner.py
@@ -0,0 +1,1041 @@
+#!/usr/bin/env python3
+
+"""
+The Rust toolchain test runner for Fuchsia.
+
+For instructions on running the compiler test suite, see
+https://doc.rust-lang.org/stable/rustc/platform-support/fuchsia.html#aarch64-fuchsia-and-x86_64-fuchsia
+"""
+
+import argparse
+from dataclasses import dataclass
+import glob
+import hashlib
+import json
+import os
+import platform
+import re
+import shutil
+import signal
+import subprocess
+import sys
+from typing import ClassVar, List
+
+
+@dataclass
+class TestEnvironment:
+    rust_dir: str
+    sdk_dir: str
+    target_arch: str
+    package_server_pid: int = None
+    emu_addr: str = None
+    libstd_name: str = None
+    libtest_name: str = None
+    verbose: bool = False
+
+    @staticmethod
+    def tmp_dir():
+        tmp_dir = os.environ.get("TEST_TOOLCHAIN_TMP_DIR")
+        if tmp_dir is not None:
+            return os.path.abspath(tmp_dir)
+        return os.path.join(os.path.dirname(__file__), "tmp~")
+
+    @classmethod
+    def env_file_path(cls):
+        return os.path.join(cls.tmp_dir(), "test_env.json")
+
+    @classmethod
+    def from_args(cls, args):
+        return cls(
+            os.path.abspath(args.rust),
+            os.path.abspath(args.sdk),
+            args.target_arch,
+            verbose=args.verbose,
+        )
+
+    @classmethod
+    def read_from_file(cls):
+        with open(cls.env_file_path(), encoding="utf-8") as f:
+            test_env = json.loads(f.read())
+            return cls(
+                test_env["rust_dir"],
+                test_env["sdk_dir"],
+                test_env["target_arch"],
+                libstd_name=test_env["libstd_name"],
+                libtest_name=test_env["libtest_name"],
+                emu_addr=test_env["emu_addr"],
+                package_server_pid=test_env["package_server_pid"],
+                verbose=test_env["verbose"],
+            )
+
+    def image_name(self):
+        if self.target_arch == "x64":
+            return "qemu-x64"
+        if self.target_arch == "arm64":
+            return "qemu-arm64"
+        raise Exception(f"Unrecognized target architecture {self.target_arch}")
+
+    def write_to_file(self):
+        with open(self.env_file_path(), "w", encoding="utf-8") as f:
+            f.write(json.dumps(self.__dict__))
+
+    def ssh_dir(self):
+        return os.path.join(self.tmp_dir(), "ssh")
+
+    def ssh_keyfile_path(self):
+        return os.path.join(self.ssh_dir(), "fuchsia_ed25519")
+
+    def ssh_authfile_path(self):
+        return os.path.join(self.ssh_dir(), "fuchsia_authorized_keys")
+
+    def vdl_output_path(self):
+        return os.path.join(self.tmp_dir(), "vdl_output")
+
+    def package_server_log_path(self):
+        return os.path.join(self.tmp_dir(), "package_server_log")
+
+    def emulator_log_path(self):
+        return os.path.join(self.tmp_dir(), "emulator_log")
+
+    def packages_dir(self):
+        return os.path.join(self.tmp_dir(), "packages")
+
+    def output_dir(self):
+        return os.path.join(self.tmp_dir(), "output")
+
+    TEST_REPO_NAME: ClassVar[str] = "rust-testing"
+
+    def repo_dir(self):
+        return os.path.join(self.tmp_dir(), self.TEST_REPO_NAME)
+
+    def rustlib_dir(self):
+        if self.target_arch == "x64":
+            return "x86_64-fuchsia"
+        if self.target_arch == "arm64":
+            return "aarch64-fuchsia"
+        raise Exception(f"Unrecognized target architecture {self.target_arch}")
+
+    def libs_dir(self):
+        return os.path.join(
+            self.rust_dir,
+            "lib",
+        )
+
+    def rustlibs_dir(self):
+        return os.path.join(
+            self.libs_dir(),
+            "rustlib",
+            self.rustlib_dir(),
+            "lib",
+        )
+
+    def sdk_arch(self):
+        machine = platform.machine()
+        if machine == "x86_64":
+            return "x64"
+        if machine == "arm":
+            return "a64"
+        raise Exception(f"Unrecognized host architecture {machine}")
+
+    def tool_path(self, tool):
+        return os.path.join(self.sdk_dir, "tools", self.sdk_arch(), tool)
+
+    def host_arch_triple(self):
+        machine = platform.machine()
+        if machine == "x86_64":
+            return "x86_64-unknown-linux-gnu"
+        if machine == "arm":
+            return "aarch64-unknown-linux-gnu"
+        raise Exception(f"Unrecognized host architecture {machine}")
+
+    def zxdb_script_path(self):
+        return os.path.join(self.tmp_dir(), "zxdb_script")
+
+    def log_info(self, msg):
+        print(msg)
+
+    def log_debug(self, msg):
+        if self.verbose:
+            print(msg)
+
+    def subprocess_output(self):
+        if self.verbose:
+            return sys.stdout
+        return subprocess.DEVNULL
+
+    def ffx_daemon_log_path(self):
+        return os.path.join(self.tmp_dir(), "ffx_daemon_log")
+
+    def ffx_isolate_dir(self):
+        return os.path.join(self.tmp_dir(), "ffx_isolate")
+
+    def ffx_home_dir(self):
+        return os.path.join(self.ffx_isolate_dir(), "user-home")
+
+    def ffx_tmp_dir(self):
+        return os.path.join(self.ffx_isolate_dir(), "tmp")
+
+    def ffx_log_dir(self):
+        return os.path.join(self.ffx_isolate_dir(), "log")
+
+    def ffx_user_config_dir(self):
+        return os.path.join(self.ffx_xdg_config_home(), "Fuchsia", "ffx", "config")
+
+    def ffx_user_config_path(self):
+        return os.path.join(self.ffx_user_config_dir(), "config.json")
+
+    def ffx_xdg_config_home(self):
+        if platform.system() == "Darwin":
+            return os.path.join(self.ffx_home_dir(), "Library", "Preferences")
+        return os.path.join(self.ffx_home_dir(), ".local", "share")
+
+    def ffx_ascendd_path(self):
+        return os.path.join(self.ffx_tmp_dir(), "ascendd")
+
+    def start_ffx_isolation(self):
+        # Most of this is translated directly from ffx's isolate library
+        os.mkdir(self.ffx_isolate_dir())
+        os.mkdir(self.ffx_home_dir())
+        os.mkdir(self.ffx_tmp_dir())
+        os.mkdir(self.ffx_log_dir())
+
+        fuchsia_dir = os.path.join(self.ffx_home_dir(), ".fuchsia")
+        os.mkdir(fuchsia_dir)
+
+        fuchsia_debug_dir = os.path.join(fuchsia_dir, "debug")
+        os.mkdir(fuchsia_debug_dir)
+
+        metrics_dir = os.path.join(fuchsia_dir, "metrics")
+        os.mkdir(metrics_dir)
+
+        analytics_path = os.path.join(metrics_dir, "analytics-status")
+        with open(analytics_path, "w", encoding="utf-8") as analytics_file:
+            print("0", file=analytics_file)
+
+        ffx_path = os.path.join(metrics_dir, "ffx")
+        with open(ffx_path, "w", encoding="utf-8") as ffx_file:
+            print("1", file=ffx_file)
+
+        os.makedirs(self.ffx_user_config_dir())
+
+        with open(
+            self.ffx_user_config_path(), "w", encoding="utf-8"
+        ) as config_json_file:
+            user_config_for_test = {
+                "log": {
+                    "enabled": True,
+                    "dir": self.ffx_log_dir(),
+                },
+                "overnet": {
+                    "socket": self.ffx_ascendd_path(),
+                },
+                "ssh": {
+                    "pub": self.ssh_authfile_path(),
+                    "priv": self.ssh_keyfile_path(),
+                },
+                "test": {
+                    "is_isolated": True,
+                    "experimental_structured_output": True,
+                },
+            }
+            print(json.dumps(user_config_for_test), file=config_json_file)
+
+        ffx_env_path = os.path.join(self.ffx_user_config_dir(), ".ffx_env")
+        with open(ffx_env_path, "w", encoding="utf-8") as ffx_env_file:
+            ffx_env_config_for_test = {
+                "user": self.ffx_user_config_path(),
+                "build": None,
+                "global": None,
+            }
+            print(json.dumps(ffx_env_config_for_test), file=ffx_env_file)
+
+        # Start ffx daemon
+        # We want this to be a long-running process that persists after the script finishes
+        # pylint: disable=consider-using-with
+        with open(
+            self.ffx_daemon_log_path(), "w", encoding="utf-8"
+        ) as ffx_daemon_log_file:
+            subprocess.Popen(
+                [
+                    self.tool_path("ffx"),
+                    "--config",
+                    self.ffx_user_config_path(),
+                    "daemon",
+                    "start",
+                ],
+                env=self.ffx_cmd_env(),
+                stdout=ffx_daemon_log_file,
+                stderr=ffx_daemon_log_file,
+            )
+
+    def ffx_cmd_env(self):
+        result = {
+            "HOME": self.ffx_home_dir(),
+            "XDG_CONFIG_HOME": self.ffx_xdg_config_home(),
+            "ASCENDD": self.ffx_ascendd_path(),
+            "FUCHSIA_SSH_KEY": self.ssh_keyfile_path(),
+            # We want to use our own specified temp directory
+            "TMP": self.tmp_dir(),
+            "TEMP": self.tmp_dir(),
+            "TMPDIR": self.tmp_dir(),
+            "TEMPDIR": self.tmp_dir(),
+        }
+
+        return result
+
+    def stop_ffx_isolation(self):
+        subprocess.check_call(
+            [
+                self.tool_path("ffx"),
+                "--config",
+                self.ffx_user_config_path(),
+                "daemon",
+                "stop",
+            ],
+            env=self.ffx_cmd_env(),
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+    def start(self):
+        """Sets up the testing environment and prepares to run tests.
+
+        Args:
+            args: The command-line arguments to this command.
+
+        During setup, this function will:
+        - Locate necessary shared libraries
+        - Create a new temp directory (this is where all temporary files are stored)
+        - Start an emulator
+        - Start an update server
+        - Create a new package repo and register it with the emulator
+        - Write test environment settings to a temporary file
+        """
+
+        # Initialize temp directory
+        if not os.path.exists(self.tmp_dir()):
+            os.mkdir(self.tmp_dir())
+        elif len(os.listdir(self.tmp_dir())) != 0:
+            raise Exception(f"Temp directory is not clean (in {self.tmp_dir()})")
+
+        os.mkdir(self.ssh_dir())
+        os.mkdir(self.output_dir())
+
+        # Find libstd and libtest
+        libstd_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libstd-*.so"))
+        libtest_paths = glob.glob(os.path.join(self.rustlibs_dir(), "libtest-*.so"))
+
+        if not libstd_paths:
+            raise Exception(f"Failed to locate libstd (in {self.rustlibs_dir()})")
+
+        if not libtest_paths:
+            raise Exception(f"Failed to locate libtest (in {self.rustlibs_dir()})")
+
+        self.libstd_name = os.path.basename(libstd_paths[0])
+        self.libtest_name = os.path.basename(libtest_paths[0])
+
+        # Generate SSH keys for the emulator to use
+        self.log_info("Generating SSH keys...")
+        subprocess.check_call(
+            [
+                "ssh-keygen",
+                "-N",
+                "",
+                "-t",
+                "ed25519",
+                "-f",
+                self.ssh_keyfile_path(),
+                "-C",
+                "Generated by test_toolchain.py",
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+        authfile_contents = subprocess.check_output(
+            [
+                "ssh-keygen",
+                "-y",
+                "-f",
+                self.ssh_keyfile_path(),
+            ],
+            stderr=self.subprocess_output(),
+        )
+        with open(self.ssh_authfile_path(), "wb") as authfile:
+            authfile.write(authfile_contents)
+
+        # Start ffx isolation
+        self.log_info("Starting ffx isolation...")
+        self.start_ffx_isolation()
+
+        # Start emulator (this will generate the vdl output)
+        self.log_info("Starting emulator...")
+        subprocess.check_call(
+            [
+                self.tool_path("fvdl"),
+                "--sdk",
+                "start",
+                "--tuntap",
+                "--headless",
+                "--nointeractive",
+                "--ssh",
+                self.ssh_dir(),
+                "--vdl-output",
+                self.vdl_output_path(),
+                "--emulator-log",
+                self.emulator_log_path(),
+                "--image-name",
+                self.image_name(),
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Parse vdl output for relevant information
+        with open(self.vdl_output_path(), encoding="utf-8") as f:
+            vdl_content = f.read()
+            matches = re.search(
+                r'network_address:\s+"\[([0-9a-f]{1,4}:(:[0-9a-f]{1,4}){4}%qemu)\]"',
+                vdl_content,
+            )
+            self.emu_addr = matches.group(1)
+
+        # Create new package repo
+        self.log_info("Creating package repo...")
+        subprocess.check_call(
+            [
+                self.tool_path("pm"),
+                "newrepo",
+                "-repo",
+                self.repo_dir(),
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Start package server
+        self.log_info("Starting package server...")
+        with open(
+            self.package_server_log_path(), "w", encoding="utf-8"
+        ) as package_server_log:
+            # We want this to be a long-running process that persists after the script finishes
+            # pylint: disable=consider-using-with
+            self.package_server_pid = subprocess.Popen(
+                [
+                    self.tool_path("pm"),
+                    "serve",
+                    "-vt",
+                    "-repo",
+                    self.repo_dir(),
+                    "-l",
+                    ":8084",
+                ],
+                stdout=package_server_log,
+                stderr=package_server_log,
+            ).pid
+
+        # Register package server with emulator
+        self.log_info("Registering package server...")
+        ssh_client = subprocess.check_output(
+            [
+                "ssh",
+                "-i",
+                self.ssh_keyfile_path(),
+                "-o",
+                "StrictHostKeyChecking=accept-new",
+                self.emu_addr,
+                "-f",
+                "echo $SSH_CLIENT",
+            ],
+            text=True,
+        )
+        repo_addr = ssh_client.split()[0].replace("%", "%25")
+        repo_url = f"http://[{repo_addr}]:8084/config.json"
+        subprocess.check_call(
+            [
+                "ssh",
+                "-i",
+                self.ssh_keyfile_path(),
+                "-o",
+                "StrictHostKeyChecking=accept-new",
+                self.emu_addr,
+                "-f",
+                f"pkgctl repo add url -f 1 -n {self.TEST_REPO_NAME} {repo_url}",
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Write to file
+        self.write_to_file()
+
+        self.log_info("Success! Your environment is ready to run tests.")
+
+    # FIXME: shardify this
+    # `facet` statement required for TCP testing via
+    # protocol `fuchsia.posix.socket.Provider`. See
+    # https://fuchsia.dev/fuchsia-src/development/testing/components/test_runner_framework?hl=en#legacy_non-hermetic_tests
+    CML_TEMPLATE: ClassVar[
+        str
+    ] = """
+    {{
+        program: {{
+            runner: "elf_test_runner",
+            binary: "bin/{exe_name}",
+            forward_stderr_to: "log",
+            forward_stdout_to: "log",
+            environ: [{env_vars}
+            ]
+        }},
+        capabilities: [
+            {{ protocol: "fuchsia.test.Suite" }},
+        ],
+        expose: [
+            {{
+                protocol: "fuchsia.test.Suite",
+                from: "self",
+            }},
+        ],
+        use: [
+            {{ storage: "data", path: "/data" }},
+            {{ protocol: [ "fuchsia.process.Launcher" ] }},
+            {{ protocol: [ "fuchsia.posix.socket.Provider" ] }}
+        ],
+        facets: {{
+            "fuchsia.test": {{ type: "system" }},
+        }},
+    }}
+    """
+
+    MANIFEST_TEMPLATE = """
+    meta/package={package_dir}/meta/package
+    meta/{package_name}.cm={package_dir}/meta/{package_name}.cm
+    bin/{exe_name}={bin_path}
+    lib/{libstd_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libstd_name}
+    lib/{libtest_name}={rust_dir}/lib/rustlib/{rustlib_dir}/lib/{libtest_name}
+    lib/ld.so.1={sdk_dir}/arch/{target_arch}/sysroot/lib/libc.so
+    lib/libzircon.so={sdk_dir}/arch/{target_arch}/sysroot/lib/libzircon.so
+    lib/libfdio.so={sdk_dir}/arch/{target_arch}/lib/libfdio.so
+    """
+
+    TEST_ENV_VARS: ClassVar[List[str]] = [
+        "TEST_EXEC_ENV",
+        "RUST_MIN_STACK",
+        "RUST_BACKTRACE",
+        "RUST_NEWRT",
+        "RUST_LOG",
+        "RUST_TEST_THREADS",
+    ]
+
+    def run(self, args):
+        """Runs the requested test in the testing environment.
+
+        Args:
+        args: The command-line arguments to this command.
+        Returns:
+        The return code of the test (0 for success, else failure).
+
+        To run a test, this function will:
+        - Create, compile, archive, and publish a test package
+        - Run the test package on the emulator
+        - Forward the test's stdout and stderr as this script's stdout and stderr
+        """
+
+        bin_path = os.path.abspath(args.bin_path)
+
+        # Build a unique, deterministic name for the test using the name of the
+        # binary and the last 6 hex digits of the hash of the full path
+        def path_checksum(path):
+            m = hashlib.sha256()
+            m.update(path.encode("utf-8"))
+            return m.hexdigest()[0:6]
+
+        base_name = os.path.basename(os.path.dirname(args.bin_path))
+        exe_name = base_name.lower().replace(".", "_")
+        package_name = f"{exe_name}_{path_checksum(bin_path)}"
+
+        package_dir = os.path.join(self.packages_dir(), package_name)
+        cml_path = os.path.join(package_dir, "meta", f"{package_name}.cml")
+        cm_path = os.path.join(package_dir, "meta", f"{package_name}.cm")
+        manifest_path = os.path.join(package_dir, f"{package_name}.manifest")
+        far_path = os.path.join(package_dir, f"{package_name}-0.far")
+
+        shared_libs = args.shared_libs[: args.n]
+        arguments = args.shared_libs[args.n :]
+
+        test_output_dir = os.path.join(self.output_dir(), package_name)
+
+        # Clean and create temporary output directory
+        if os.path.exists(test_output_dir):
+            shutil.rmtree(test_output_dir)
+
+        os.mkdir(test_output_dir)
+
+        # Open log file
+        log_path = os.path.join(test_output_dir, "log")
+        with open(log_path, "w", encoding="utf-8") as log_file:
+
+            def log(msg):
+                print(msg, file=log_file)
+                log_file.flush()
+
+            log(f"Bin path: {bin_path}")
+
+            log("Setting up package...")
+
+            # Set up package
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "-o",
+                    package_dir,
+                    "-n",
+                    package_name,
+                    "init",
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Writing CML...")
+
+            # Write and compile CML
+            with open(cml_path, "w", encoding="utf-8") as cml:
+                # Collect environment variables
+                env_vars = ""
+                for var_name in self.TEST_ENV_VARS:
+                    var_value = os.getenv(var_name)
+                    if var_value is not None:
+                        env_vars += f'\n            "{var_name}={var_value}",'
+
+                # Default to no backtrace for test suite
+                if os.getenv("RUST_BACKTRACE") == None:
+                    env_vars += f'\n            "RUST_BACKTRACE=0",'
+
+                cml.write(
+                    self.CML_TEMPLATE.format(env_vars=env_vars, exe_name=exe_name)
+                )
+
+            log("Compiling CML...")
+
+            subprocess.check_call(
+                [
+                    self.tool_path("cmc"),
+                    "compile",
+                    cml_path,
+                    "--includepath",
+                    ".",
+                    "--output",
+                    cm_path,
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Writing manifest...")
+
+            # Write, build, and archive manifest
+            with open(manifest_path, "w", encoding="utf-8") as manifest:
+                manifest.write(
+                    self.MANIFEST_TEMPLATE.format(
+                        bin_path=bin_path,
+                        exe_name=exe_name,
+                        package_dir=package_dir,
+                        package_name=package_name,
+                        rust_dir=self.rust_dir,
+                        rustlib_dir=self.rustlib_dir(),
+                        sdk_dir=self.sdk_dir,
+                        libstd_name=self.libstd_name,
+                        libtest_name=self.libtest_name,
+                        target_arch=self.target_arch,
+                    )
+                )
+                for shared_lib in shared_libs:
+                    manifest.write(f"lib/{os.path.basename(shared_lib)}={shared_lib}\n")
+
+            log("Compiling and archiving manifest...")
+
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "-o",
+                    package_dir,
+                    "-m",
+                    manifest_path,
+                    "build",
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "-o",
+                    package_dir,
+                    "-m",
+                    manifest_path,
+                    "archive",
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Publishing package to repo...")
+
+            # Publish package to repo
+            subprocess.check_call(
+                [
+                    self.tool_path("pm"),
+                    "publish",
+                    "-a",
+                    "-repo",
+                    self.repo_dir(),
+                    "-f",
+                    far_path,
+                ],
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Running ffx test...")
+
+            # Run test on emulator
+            subprocess.run(
+                [
+                    self.tool_path("ffx"),
+                    "--config",
+                    self.ffx_user_config_path(),
+                    "test",
+                    "run",
+                    f"fuchsia-pkg://{self.TEST_REPO_NAME}/{package_name}#meta/{package_name}.cm",
+                    "--min-severity-logs",
+                    "TRACE",
+                    "--output-directory",
+                    test_output_dir,
+                    "--",
+                ]
+                + arguments,
+                env=self.ffx_cmd_env(),
+                check=False,
+                stdout=log_file,
+                stderr=log_file,
+            )
+
+            log("Reporting test suite output...")
+
+            # Read test suite output
+            run_summary_path = os.path.join(test_output_dir, "run_summary.json")
+            if os.path.exists(run_summary_path):
+                with open(run_summary_path, encoding="utf-8") as f:
+                    run_summary = json.loads(f.read())
+
+                suite = run_summary["data"]["suites"][0]
+                case = suite["cases"][0]
+
+                return_code = 0 if case["outcome"] == "PASSED" else 1
+
+                artifacts = case["artifacts"]
+                artifact_dir = case["artifact_dir"]
+                stdout_path = None
+                stderr_path = None
+
+                for path, artifact in artifacts.items():
+                    artifact_path = os.path.join(test_output_dir, artifact_dir, path)
+                    artifact_type = artifact["artifact_type"]
+
+                    if artifact_type == "STDERR":
+                        stderr_path = artifact_path
+                    elif artifact_type == "STDOUT":
+                        stdout_path = artifact_path
+
+                if stdout_path is not None and os.path.exists(stdout_path):
+                    with open(stdout_path, encoding="utf-8") as f:
+                        print(f.read(), file=sys.stdout, end="")
+
+                if stderr_path is not None and os.path.exists(stderr_path):
+                    with open(stderr_path, encoding="utf-8") as f:
+                        print(f.read(), file=sys.stderr, end="")
+            else:
+                log("Failed to open test run summary")
+                return_code = 254
+
+            log("Done!")
+
+        return return_code
+
+    def stop(self):
+        """Shuts down and cleans up the testing environment.
+
+        Args:
+        args: The command-line arguments to this command.
+        Returns:
+        The return code of the test (0 for success, else failure).
+
+        During cleanup, this function will stop the emulator, package server, and
+        update server, then delete all temporary files. If an error is encountered
+        while stopping any running processes, the temporary files will not be deleted.
+        Passing --delete-tmp will force the process to delete the files anyway.
+        """
+
+        self.log_debug("Reporting logs...")
+
+        # Print test log files
+        for test_dir in os.listdir(self.output_dir()):
+            log_path = os.path.join(self.output_dir(), test_dir, "log")
+            self.log_debug(f"\n---- Logs for test '{test_dir}' ----\n")
+            if os.path.exists(log_path):
+                with open(log_path, encoding="utf-8") as log:
+                    self.log_debug(log.read())
+            else:
+                self.log_debug("No logs found")
+
+        # Print the emulator log
+        self.log_debug("\n---- Emulator logs ----\n")
+        if os.path.exists(self.emulator_log_path()):
+            with open(self.emulator_log_path(), encoding="utf-8") as log:
+                self.log_debug(log.read())
+        else:
+            self.log_debug("No emulator logs found")
+
+        # Print the package server log
+        self.log_debug("\n---- Package server log ----\n")
+        if os.path.exists(self.package_server_log_path()):
+            with open(self.package_server_log_path(), encoding="utf-8") as log:
+                self.log_debug(log.read())
+        else:
+            self.log_debug("No package server log found")
+
+        # Print the ffx daemon log
+        self.log_debug("\n---- ffx daemon log ----\n")
+        if os.path.exists(self.ffx_daemon_log_path()):
+            with open(self.ffx_daemon_log_path(), encoding="utf-8") as log:
+                self.log_debug(log.read())
+        else:
+            self.log_debug("No ffx daemon log found")
+
+        # Stop package server
+        self.log_info("Stopping package server...")
+        os.kill(self.package_server_pid, signal.SIGTERM)
+
+        # Shut down the emulator
+        self.log_info("Stopping emulator...")
+        subprocess.check_call(
+            [
+                self.tool_path("fvdl"),
+                "--sdk",
+                "kill",
+                "--launched-proto",
+                self.vdl_output_path(),
+            ],
+            stdout=self.subprocess_output(),
+            stderr=self.subprocess_output(),
+        )
+
+        # Stop ffx isolation
+        self.log_info("Stopping ffx isolation...")
+        self.stop_ffx_isolation()
+
+    def delete_tmp(self):
+        # Remove temporary files
+        self.log_info("Deleting temporary files...")
+        shutil.rmtree(self.tmp_dir(), ignore_errors=True)
+
+    def debug(self, args):
+        command = [
+            self.tool_path("ffx"),
+            "--config",
+            self.ffx_user_config_path(),
+            "debug",
+            "connect",
+            "--",
+            "--build-id-dir",
+            os.path.join(self.sdk_dir, ".build-id"),
+            "--build-id-dir",
+            os.path.join(self.libs_dir(), ".build-id"),
+        ]
+
+        # Add rust source if it's available
+        if args.rust_src is not None:
+            command += [
+                "--build-dir",
+                args.rust_src,
+            ]
+
+        # Add fuchsia source if it's available
+        if args.fuchsia_src is not None:
+            command += [
+                "--build-dir",
+                os.path.join(args.fuchsia_src, "out", "default"),
+            ]
+
+        # Load debug symbols for the test binary and automatically attach
+        if args.test is not None:
+            if args.rust_src is None:
+                raise Exception(
+                    "A Rust source path is required with the `test` argument"
+                )
+
+            test_name = os.path.splitext(os.path.basename(args.test))[0]
+
+            build_dir = os.path.join(
+                args.rust_src,
+                "fuchsia-build",
+                self.host_arch_triple(),
+            )
+            test_dir = os.path.join(
+                build_dir,
+                "test",
+                os.path.dirname(args.test),
+                test_name,
+            )
+
+            with open(self.zxdb_script_path(), mode="w", encoding="utf-8") as f:
+                print(f"attach {test_name[:31]}", file=f)
+
+            command += [
+                "--symbol-path",
+                test_dir,
+                "-S",
+                self.zxdb_script_path(),
+            ]
+
+        # Add any other zxdb arguments the user passed
+        if args.zxdb_args is not None:
+            command += args.zxdb_args
+
+        # Connect to the running emulator with zxdb
+        subprocess.run(command, env=self.ffx_cmd_env(), check=False)
+
+
+def start(args):
+    test_env = TestEnvironment.from_args(args)
+    test_env.start()
+    return 0
+
+
+def run(args):
+    test_env = TestEnvironment.read_from_file()
+    return test_env.run(args)
+
+
+def stop(args):
+    test_env = TestEnvironment.read_from_file()
+    test_env.stop()
+    if not args.no_delete:
+        test_env.delete_tmp()
+    return 0
+
+
+def delete_tmp(args):
+    del args
+    test_env = TestEnvironment.read_from_file()
+    test_env.delete_tmp()
+    return 0
+
+
+def debug(args):
+    test_env = TestEnvironment.read_from_file()
+    test_env.debug(args)
+    return 0
+
+
+def main():
+    parser = argparse.ArgumentParser()
+
+    def print_help(args):
+        del args
+        parser.print_help()
+        return 0
+
+    parser.set_defaults(func=print_help)
+
+    subparsers = parser.add_subparsers(help="valid sub-commands")
+
+    start_parser = subparsers.add_parser(
+        "start", help="initializes the testing environment"
+    )
+    start_parser.add_argument(
+        "--rust",
+        help="the directory of the installed Rust compiler for Fuchsia",
+        required=True,
+    )
+    start_parser.add_argument(
+        "--sdk",
+        help="the directory of the fuchsia SDK",
+        required=True,
+    )
+    start_parser.add_argument(
+        "--verbose",
+        help="prints more output from executed processes",
+        action="store_true",
+    )
+    start_parser.add_argument(
+        "--target-arch",
+        help="the architecture of the image to test",
+        required=True,
+    )
+    start_parser.set_defaults(func=start)
+
+    run_parser = subparsers.add_parser(
+        "run", help="run a test in the testing environment"
+    )
+    run_parser.add_argument(
+        "n", help="the number of shared libs passed along with the executable", type=int
+    )
+    run_parser.add_argument("bin_path", help="path to the binary to run")
+    run_parser.add_argument(
+        "shared_libs",
+        help="the shared libs passed along with the binary",
+        nargs=argparse.REMAINDER,
+    )
+    run_parser.set_defaults(func=run)
+
+    stop_parser = subparsers.add_parser(
+        "stop", help="shuts down and cleans up the testing environment"
+    )
+    stop_parser.add_argument(
+        "--no-delete",
+        default=False,
+        action="store_true",
+        help="don't delete temporary files after stopping",
+    )
+    stop_parser.set_defaults(func=stop)
+
+    delete_parser = subparsers.add_parser(
+        "delete-tmp",
+        help="deletes temporary files after the testing environment has been manually cleaned up",
+    )
+    delete_parser.set_defaults(func=delete_tmp)
+
+    debug_parser = subparsers.add_parser(
+        "debug",
+        help="connect to the active testing environment with zxdb",
+    )
+    debug_parser.add_argument(
+        "--rust-src",
+        default=None,
+        help="the path to the Rust source being tested",
+    )
+    debug_parser.add_argument(
+        "--fuchsia-src",
+        default=None,
+        help="the path to the Fuchsia source",
+    )
+    debug_parser.add_argument(
+        "--test",
+        default=None,
+        help="the path to the test to debug (e.g. ui/box/new.rs)",
+    )
+    debug_parser.add_argument(
+        "zxdb_args",
+        default=None,
+        nargs=argparse.REMAINDER,
+        help="any additional arguments to pass to zxdb",
+    )
+    debug_parser.set_defaults(func=debug)
+
+    args = parser.parse_args()
+    return args.func(args)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index f5a49410ea5..7e355b7fccf 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -210,8 +210,8 @@ metrics.
 
 ## link-self-contained
 
-On targets that support it this flag controls whether the linker will use libraries and objects
-shipped with Rust instead or those in the system.
+On `windows-gnu`, `linux-musl`, and `wasi` targets, this flag controls whether the
+linker will use libraries and objects shipped with Rust instead or those in the system.
 It takes one of the following values:
 
 * no value: rustc will use heuristic to disable self-contained mode if system has necessary tools.
diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md
index 1ff6003c121..5de29b35e6b 100644
--- a/src/doc/rustc/src/platform-support/fuchsia.md
+++ b/src/doc/rustc/src/platform-support/fuchsia.md
@@ -641,8 +641,66 @@ available on the [Fuchsia devsite].
 
 ### Running the compiler test suite
 
-Running the Rust test suite on Fuchsia is [not currently supported], but work is
-underway to enable it.
+Pre-requisites for running the Rust test suite on Fuchsia are:
+1. Checkout of Rust source.
+1. Setup of `config-env.sh` and `config.toml` from "[Targeting Fuchsia with a compiler built from source](#targeting-fuchsia-with-a-compiler-built-from-source)".
+1. Download of the Fuchsia SDK. Minimum supported SDK version is [9.20220726.1.1](https://chrome-infra-packages.appspot.com/p/fuchsia/sdk/core/linux-amd64/+/version:9.20220726.1.1)
+
+Interfacing with the Fuchsia emulator is handled by our test runner script located
+at `${RUST_SRC_PATH}/src/ci/docker/scripts/fuchsia-test-runner.py`.
+
+We start by activating our Fuchsia test environment. From a terminal:
+
+**Issue command from ${RUST_SRC_PATH}**
+```sh
+src/ci/docker/scripts/fuchsia-test-runner.py start
+    --rust .
+    --sdk ${SDK_PATH}
+    --target-arch {x64,arm64}
+```
+
+Next, for ease of commands, we copy `config-env.sh` and `config.toml` into our Rust source
+code path, `${RUST_SRC_PATH}`.
+
+From there, we utilize `x.py` to run our tests, using the test runner script to
+run the tests on our emulator. To run the full `src/test/ui` test suite:
+
+**Run from ${RUST_SRC_PATH}**
+```sh
+( \
+    source config-env.sh &&                                                   \
+    ./x.py                                                                    \
+    --config config.toml                                                      \
+    --stage=2                                                                 \
+    test src/test/ui                                                          \
+    --target x86_64-fuchsia                                                   \
+    --run=always --jobs 1                                                     \
+    --test-args --target-rustcflags                                           \
+    --test-args -L                                                            \
+    --test-args --target-rustcflags                                           \
+    --test-args ${SDK_PATH}/arch/{x64|arm64}/sysroot/lib                      \
+    --test-args --target-rustcflags                                           \
+    --test-args -L                                                            \
+    --test-args --target-rustcflags                                           \
+    --test-args ${SDK_PATH}/arch/{x64|arm64}/lib                              \
+    --test-args --target-rustcflags                                           \
+    --test-args -Cpanic=abort                                                 \
+    --test-args --target-rustcflags                                           \
+    --test-args -Zpanic_abort_tests                                           \
+    --test-args --remote-test-client                                          \
+    --test-args src/ci/docker/scripts/fuchsia-test-runner.py                  \
+)
+```
+
+*Note: The test suite cannot be run in parallel at the moment, so `x.py`
+must be run with `--jobs 1` to ensure only one test runs at a time.*
+
+When finished, stop the test environment:
+
+**Issue command from ${RUST_SRC_PATH}**
+```sh
+src/ci/docker/scripts/fuchsia-test-runner.py stop
+```
 
 ## Debugging
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 56a873e3e82..c20595614b0 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1942,6 +1942,79 @@ 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> {
+    map: rustc_middle::hir::map::Map<'hir>,
+    item: Option<&'hir hir::Item<'hir>>,
+    looking_for: Ident,
+    target_hir_id: hir::HirId,
+}
+
+impl<'hir> OneLevelVisitor<'hir> {
+    fn new(map: rustc_middle::hir::map::Map<'hir>, target_hir_id: hir::HirId) -> Self {
+        Self { map, item: None, looking_for: Ident::empty(), target_hir_id }
+    }
+
+    fn reset(&mut self, looking_for: Ident) {
+        self.looking_for = looking_for;
+        self.item = None;
+    }
+}
+
+impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
+    type NestedFilter = rustc_middle::hir::nested_filter::All;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.map
+    }
+
+    fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
+        if self.item.is_none()
+            && item.ident == self.looking_for
+            && matches!(item.kind, hir::ItemKind::Use(_, _))
+            || item.hir_id() == self.target_hir_id
+        {
+            self.item = Some(item);
+        }
+    }
+}
+
+/// Because a `Use` item directly links to the imported item, we need to manually go through each
+/// import one by one. To do so, we go to the parent item and look for the `Ident` into it. Then,
+/// if we found the "end item" (the imported one), we stop there because we don't need its
+/// documentation. Otherwise, we repeat the same operation until we find the "end item".
+fn get_all_import_attributes<'hir>(
+    mut item: &hir::Item<'hir>,
+    tcx: TyCtxt<'hir>,
+    target_hir_id: hir::HirId,
+    attributes: &mut Vec<ast::Attribute>,
+) {
+    let hir_map = tcx.hir();
+    let mut visitor = OneLevelVisitor::new(hir_map, target_hir_id);
+    // If the item is an import and has at least a path with two parts, we go into it.
+    while let hir::ItemKind::Use(path, _) = item.kind &&
+        path.segments.len() > 1 &&
+        let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res
+    {
+        if let Some(hir::Node::Item(parent_item)) = hir_map.get_if_local(def_id) {
+            // We add the attributes from this import into the list.
+            attributes.extend_from_slice(hir_map.attrs(item.hir_id()));
+            // 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);
+            hir::intravisit::walk_item(&mut visitor, parent_item);
+            if let Some(i) = visitor.item {
+                item = i;
+            } else {
+                break;
+            }
+        } else {
+            break;
+        }
+    }
+}
+
 fn clean_maybe_renamed_item<'tcx>(
     cx: &mut DocContext<'tcx>,
     item: &hir::Item<'tcx>,
@@ -2023,13 +2096,20 @@ fn clean_maybe_renamed_item<'tcx>(
             }
             _ => unreachable!("not yet converted"),
         };
-        if let Some(import_id) = import_id {
-            let (attrs, cfg) = inline::merge_attrs(
-                cx,
-                Some(cx.tcx.parent_module(import_id).to_def_id()),
-                inline::load_attrs(cx, def_id),
-                Some(inline::load_attrs(cx, cx.tcx.hir().local_def_id(import_id).to_def_id())),
-            );
+
+        let mut extra_attrs = Vec::new();
+        if let Some(hir::Node::Item(use_node)) =
+            import_id.and_then(|hir_id| cx.tcx.hir().find(hir_id))
+        {
+            // We get all the various imports' attributes.
+            get_all_import_attributes(use_node, cx.tcx, item.hir_id(), &mut extra_attrs);
+        }
+
+        if !extra_attrs.is_empty() {
+            extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
+            let attrs = Attributes::from_ast(&extra_attrs);
+            let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
+
             vec![Item::from_def_id_and_attrs_and_parts(
                 def_id,
                 Some(name),
@@ -2045,7 +2125,7 @@ fn clean_maybe_renamed_item<'tcx>(
 
 fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
     let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx));
-    Item::from_hir_id_and_parts(variant.id, Some(variant.ident.name), kind, cx)
+    Item::from_hir_id_and_parts(variant.hir_id, Some(variant.ident.name), kind, cx)
 }
 
 fn clean_impl<'tcx>(
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 7cbe2f1e227..cb50c3ae829 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1293,7 +1293,7 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
     }
 
     fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) {
-        self.visit_testable(v.ident.to_string(), v.id, v.span, |this| {
+        self.visit_testable(v.ident.to_string(), v.hir_id, v.span, |this| {
             intravisit::walk_variant(this, v);
         });
     }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 3f295b96dc5..9b1cac85cfd 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -159,7 +159,6 @@ h1.fqn {
 .main-heading {
 	display: flex;
 	flex-wrap: wrap;
-	justify-content: space-between;
 	padding-bottom: 6px;
 	margin-bottom: 15px;
 }
@@ -803,7 +802,6 @@ table,
 	align-items: baseline;
 }
 #crate-search-div {
-	display: inline-block;
 	/* ensures that 100% in properties of #crate-search-div:after
 	are relative to the size of this div */
 	position: relative;
@@ -920,7 +918,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
 }
 
 .popover {
-	font-size: 1rem;
 	position: absolute;
 	right: 0;
 	z-index: 2;
@@ -928,7 +925,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	margin-top: 7px;
 	border-radius: 3px;
 	border: 1px solid var(--border-color);
-	font-size: 1rem;
 	--popover-arrow-offset: 11px;
 }
 
@@ -1051,7 +1047,6 @@ so that we can apply CSS-filters to change the arrow color in themes */
 
 .rightside {
 	padding-left: 12px;
-	padding-right: 2px;
 	float: right;
 }
 
@@ -1262,10 +1257,6 @@ h3.variant {
 	margin-left: 24px;
 }
 
-:target > code, :target > .code-header {
-	opacity: 1;
-}
-
 :target {
 	padding-right: 3px;
 	background-color: var(--target-background-color);
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 0538762e44d..874f130d7e2 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -47,10 +47,8 @@ function blurHandler(event, parentElem, hideCallback) {
     }
 }
 
-(function() {
-    window.rootPath = getVar("root-path");
-    window.currentCrate = getVar("current-crate");
-}());
+window.rootPath = getVar("root-path");
+window.currentCrate = getVar("current-crate");
 
 function setMobileTopbar() {
     // FIXME: It would be nicer to generate this text content directly in HTML,
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index dd0531c5e70..4999bb35994 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1491,6 +1491,7 @@ function initSearch(rawSearchIndex) {
         const target = searchState.focusedByTab[searchState.currentTab] ||
             document.querySelectorAll(".search-results.active a").item(0) ||
             document.querySelectorAll("#titles > button").item(searchState.currentTab);
+        searchState.focusedByTab[searchState.currentTab] = null;
         if (target) {
             target.focus();
         }
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index afe920b7fa1..b4d4150cddb 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -19,9 +19,13 @@ impl StaticFile {
     }
 
     pub(crate) fn minified(&self) -> Vec<u8> {
-        if self.filename.ends_with(".css") {
+        let extension = match self.filename.extension() {
+            Some(e) => e,
+            None => return self.bytes.to_owned(),
+        };
+        if extension == "css" {
             minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into()
-        } else if self.filename.ends_with(".js") {
+        } else if extension == "js" {
             minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into()
         } else {
             self.bytes.to_owned()
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 091a1ba70ca..acfbd072121 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -674,7 +674,7 @@ impl FromWithTcx<clean::Variant> for Variant {
 impl FromWithTcx<clean::Discriminant> for Discriminant {
     fn from_tcx(disr: clean::Discriminant, tcx: TyCtxt<'_>) -> Self {
         Discriminant {
-            // expr is only none if going throught the inlineing path, which gets
+            // expr is only none if going through the inlineing path, which gets
             // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
             // the expr is always some.
             expr: disr.expr(tcx).unwrap(),
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index d13efe6c113..beb70540091 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -277,7 +277,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
 
         let e = ExternalCrate { crate_num: LOCAL_CRATE };
 
-        // FIXME(adotinthevoid): Remove this, as it's not consistant with not
+        // FIXME(adotinthevoid): Remove this, as it's not consistent with not
         // inlining foreign items.
         let foreign_trait_items = self.get_trait_items();
         let mut index = (*self.index).clone().into_inner();
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 48835abf952..02b22789608 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -244,10 +244,10 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
                         matches!(
                             node,
                             hir::Node::Variant(hir::Variant {
-                                data: hir::VariantData::Tuple(_, _),
+                                data: hir::VariantData::Tuple(_, _, _),
                                 ..
                             }) | hir::Node::Item(hir::Item {
-                                kind: hir::ItemKind::Struct(hir::VariantData::Tuple(_, _), _),
+                                kind: hir::ItemKind::Struct(hir::VariantData::Tuple(_, _, _), _),
                                 ..
                             })
                         )
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
index d121a3e2aa4..1b373cfe5bb 100644
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs
@@ -354,7 +354,14 @@ impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
             self.parent_scope.module = old_module;
         } else {
             match &item.kind {
-                ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
+                ItemKind::Impl(box ast::Impl { of_trait: Some(trait_ref), .. }) => {
+                    if let Some(partial_res) = self.resolver.get_partial_res(trait_ref.ref_id)
+                        && let Some(res) = partial_res.full_res()
+                        && let Some(trait_def_id) = res.opt_def_id()
+                        && !trait_def_id.is_local()
+                        && self.visited_mods.insert(trait_def_id) {
+                        self.resolve_doc_links_extern_impl(trait_def_id, false);
+                    }
                     self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
                 }
                 ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 4bc91fc4030..817b3e48419 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -53,7 +53,7 @@ pub struct ItemSummary {
     /// `["std", "io", "lazy", "Lazy"]` for `std::io::lazy::Lazy`).
     ///
     /// Note that items can appear in multiple paths, and the one chosen is implementation
-    /// defined. Currenty, this is the full path to where the item was defined. Eg
+    /// defined. Currently, this is the full path to where the item was defined. Eg
     /// [`String`] is currently `["alloc", "string", "String"]` and [`HashMap`] is
     /// `["std", "collections", "hash", "map", "HashMap"]`, but this is subject to change.
     pub path: Vec<String>,
@@ -351,7 +351,7 @@ pub enum Variant {
     /// A variant with unnamed fields.
     ///
     /// Unlike most of json, `#[doc(hidden)]` fields will be given as `None`
-    /// instead of being ommited, because order matters.
+    /// instead of being omitted, because order matters.
     ///
     /// ```rust
     /// enum Demo {
diff --git a/src/stage0.json b/src/stage0.json
index 72308d50c8e..c3f50272b67 100644
--- a/src/stage0.json
+++ b/src/stage0.json
@@ -17,349 +17,349 @@
     "tool is executed."
   ],
   "compiler": {
-    "date": "2022-09-20",
+    "date": "2022-11-01",
     "version": "beta"
   },
   "rustfmt": {
-    "date": "2022-09-20",
+    "date": "2022-11-01",
     "version": "nightly"
   },
   "checksums_sha256": {
-    "dist/2022-09-20/cargo-beta-aarch64-apple-darwin.tar.gz": "3186f69cc7efaf3f933ad77798ddf58bf11c0719dc1dec53fadc502a236ef753",
-    "dist/2022-09-20/cargo-beta-aarch64-apple-darwin.tar.xz": "5ad195346a21a80c700ca08223060ea66298fe8e4cbac19148d14b92a9319b01",
-    "dist/2022-09-20/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "6a7647d761ce3adba9d4ceff2e6c1929e9d96d767961a7a062f41ec09a1abb85",
-    "dist/2022-09-20/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "13566a68dd2000fb33a990c21b62b82e77d1bd1f3384152f439cf96318f07f3e",
-    "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "cc698fe69e27a077c6d2aa8dc7319847b1ecd78ad4e3519161957c7cab90c592",
-    "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "1cd369b8ab90e85da78784cf08a92aee87f0804b448676637ee48cb3409dc026",
-    "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "856170acebfd7900448fe02bd835d633b2930e2401c4211d72e5dd8c38943606",
-    "dist/2022-09-20/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "5bbc32a426071c84d39395c64e1f9cfe0db29ab10c255c2a8a8f748b624cdb7a",
-    "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "50518c889d2408a7edf524c0340c8ff6881fd14f505dca0d419deefdb94c3afb",
-    "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "8143057e446c169e614c153ffbe2428e94404af96d06b1d3103028f695388211",
-    "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "987064edfdb30dde07ef9b2cbd072f66ca042bf95ae724909cafbc13bcf69885",
-    "dist/2022-09-20/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "253161f50a399818a77360f97443ef818dfca3d384e86048655b08c8a799bafc",
-    "dist/2022-09-20/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "7b9a0feee8f3e1e3c58df38f947904d76006c938a2395650e094337ede9918e9",
-    "dist/2022-09-20/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "2194b642b8ed595b8534ded204a72f910215c8f42482ac64644f784a3b2ae8b3",
-    "dist/2022-09-20/cargo-beta-i686-pc-windows-gnu.tar.gz": "7cc2c490988dd1ae095198664317cb0b5c8071fc31bf49aed1eca21eb2766cd8",
-    "dist/2022-09-20/cargo-beta-i686-pc-windows-gnu.tar.xz": "32641252e12cdadf0a232b43103fc56af621a48056864ff2ee9a034dd2da8f1f",
-    "dist/2022-09-20/cargo-beta-i686-pc-windows-msvc.tar.gz": "32581e6bf22e7d2c7a147a87158161e3fa07f46ec0252e632a3bafb824382a28",
-    "dist/2022-09-20/cargo-beta-i686-pc-windows-msvc.tar.xz": "a9a68905a4540389d28e40cc2137cf2fcca77c425089cd99072a34ba15e3ab6a",
-    "dist/2022-09-20/cargo-beta-i686-unknown-linux-gnu.tar.gz": "b5d9ecd7be4ab25919cd0731bb28a2612965943c5ccedf35ac09c169ed2c97db",
-    "dist/2022-09-20/cargo-beta-i686-unknown-linux-gnu.tar.xz": "387f7d95d04503293f708f65821f55878449eb5a0efe3344005dca18b84e6563",
-    "dist/2022-09-20/cargo-beta-mips-unknown-linux-gnu.tar.gz": "f8fbf21aac677276cdf246748d59d183e566bfcacabcd3eab6f19d6c857193ef",
-    "dist/2022-09-20/cargo-beta-mips-unknown-linux-gnu.tar.xz": "70e561d77632d1463839a8ea9cb72ff49afb61dbba95fa321bdba74be384b21f",
-    "dist/2022-09-20/cargo-beta-mips64-unknown-linux-gnuabi64.tar.gz": "e81b5a5fc70a1f7ed5920a3860b1259a2cecd9a1d981f2a564cd936de53ecf8a",
-    "dist/2022-09-20/cargo-beta-mips64-unknown-linux-gnuabi64.tar.xz": "ef9de44d1d37812bfeb67b353f1e308bf46d62c9fe191980de3a62fbfe5167a4",
-    "dist/2022-09-20/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "68c1cd309775626f19431f7dbb73789b17ee629b588e05bf0231313913cb6a8a",
-    "dist/2022-09-20/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "db290a98987c8bd527f1efe9ff09055fdce8eea0673d2c5eba0640649396b8d0",
-    "dist/2022-09-20/cargo-beta-mipsel-unknown-linux-gnu.tar.gz": "f4a7da60f164c03bd274f8c98b58a524d7a73476c726e2ef5695f2be950421c7",
-    "dist/2022-09-20/cargo-beta-mipsel-unknown-linux-gnu.tar.xz": "8fc46e05ba0830eec88e1d764b02fb9a4836883fd180800a8edd3a4cf0acbdae",
-    "dist/2022-09-20/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "5f528af9436bfa31d559544220fcb59001a90bff18363390f7fab82f259defde",
-    "dist/2022-09-20/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "502d6da5158075cd997833314f9ca7a527aecc8e28c9e26ee9796c2e9ac91cf5",
-    "dist/2022-09-20/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "16a7b922fa6c6019541e859386dbd38e64507d951376c847f83c6b983c72b417",
-    "dist/2022-09-20/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "7f3e361f9bfcdb5fd765f86ed372e00df62af4ae5714d9a2b3324f3929518677",
-    "dist/2022-09-20/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "f38265c64d6ac34d4632f38368d910bd9471aaf8736595623126cb53e810e307",
-    "dist/2022-09-20/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "99e7f3795aea0abb019b80b1f35aa8e1638fee35e548424ca52fd23c5bf82c71",
-    "dist/2022-09-20/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "bb9e89b65fca9a1fad5293e8a52b27331f08e9660237c0b1e7f750064d45ab1d",
-    "dist/2022-09-20/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "ea81188084da2f2771a1d9414c20065a19544b1af0e56dc71eae7ca00ff72708",
-    "dist/2022-09-20/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "745577c8c52065d84cedd32608ca0e17f1a46bb86b4d619cd01785486dc99480",
-    "dist/2022-09-20/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "85944275d5d05943c89ebf8e487bee35ed8586aa9f1903f83490c12ba74ad8cf",
-    "dist/2022-09-20/cargo-beta-x86_64-apple-darwin.tar.gz": "6c3f841c718404d4917353c7fefee7491df62d7456633bfb99dc850a49aab285",
-    "dist/2022-09-20/cargo-beta-x86_64-apple-darwin.tar.xz": "0c5a5c3ceec9fcf3b8dbb9fd10172251622e873671049b042b55ede34b8797a8",
-    "dist/2022-09-20/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "385a01c7a11a5f51253e8182d82763295037d625e7bcf27d54b5f0349cea488c",
-    "dist/2022-09-20/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "fe498e30198a43586be82c6fbd794093299eddba51fd668d81aed88bef0471ae",
-    "dist/2022-09-20/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "3ee6926fd5f491eecf574aba631d09d97a9332b936eb7bb0ab348ac3fa02db02",
-    "dist/2022-09-20/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "d15a8c24d04b308b91d9114b583087e2a13e75920a1837a78fe330cf6892ce4e",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-freebsd.tar.gz": "89368ca5eae65a569ae98e66834e93b240ef43a007e02e768ae9bbd5de4a4cf6",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-freebsd.tar.xz": "20b46e126c900892d576e972e39409c9009bafa4b3c159e624179d77afa912a9",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-illumos.tar.gz": "1712fd404274c993b95aa44dea6b9ff3b0f9857d8d1646e0cbf454d3386f5e32",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-illumos.tar.xz": "bd3f848d22bfa19060d459167b6154cc79070e0066f8da79587390fb92404288",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "0e5869b406dbaab0ef123459a93d4d6a34e85e9bd72d8a206bef69aac9e84d5c",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "5b0b255fb82d0e751187c6cc6b64298ca014ef86782984ef9e57a0b2ab373f24",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "09dcaeb783d7c57aa8c708295cf46bdcb3873a20ca30794b3c1a8797b2cc9476",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "918ca6c81e9e19f9d84d80e508af0050f2ec2ce2d0d0aa40cd3afd524d69917b",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-netbsd.tar.gz": "e66e3ecf93bad48573cf34ed44d508908370a8cc0c2764001cddbef022fb6e73",
-    "dist/2022-09-20/cargo-beta-x86_64-unknown-netbsd.tar.xz": "03514df0f9f2193824e227c19b84f282d7cb90145206bcfa21cf4f8223047462",
-    "dist/2022-09-20/rust-std-beta-aarch64-apple-darwin.tar.gz": "7a6a03adcf6481d90d39e929f99a50ed170e98fe61c3fae5264c3aa4d99530ca",
-    "dist/2022-09-20/rust-std-beta-aarch64-apple-darwin.tar.xz": "ccc8d4fb07a0a9c57b60d05bcf6d076a8b3cdb397930182ebfe99a2e5cd629da",
-    "dist/2022-09-20/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "3e70853d9fbc3dab4a39303b2281ad63d36a9ae2fd8d6bb7d96f184644e20531",
-    "dist/2022-09-20/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "59bb5bb6cd8d7269bfd29a952cd26280f5091fb24af4e7bf10cd59b80323d85a",
-    "dist/2022-09-20/rust-std-beta-aarch64-apple-ios.tar.gz": "da321d56c24e6c2aa326cc082912498c27231f0f0fea27ab925807108d6f329e",
-    "dist/2022-09-20/rust-std-beta-aarch64-apple-ios.tar.xz": "21e78983abd4523ac9efb0405734ebfd6c8c09b9cc89b9d052b1a58bb7ab798c",
-    "dist/2022-09-20/rust-std-beta-aarch64-fuchsia.tar.gz": "dd64a476c35b1a6aefed6bcc756cb4562a60ec0277e5661241018678d7a04268",
-    "dist/2022-09-20/rust-std-beta-aarch64-fuchsia.tar.xz": "bcbb6f3457c9b6e1c9109094536445ff208e78b5a24485af6de580ba7e279861",
-    "dist/2022-09-20/rust-std-beta-aarch64-linux-android.tar.gz": "63ab6db951f5cefbd1ab1661ffbac9749fec8d4165047dfbcb76b7dcb1468e48",
-    "dist/2022-09-20/rust-std-beta-aarch64-linux-android.tar.xz": "f3e89fe21779ecd8373280f38e29db8941c0836cba7314414d854ba685e075e4",
-    "dist/2022-09-20/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "fa187421633e7ca45948258e804fd4a8177070b9a4b964ac95231018cd5e724c",
-    "dist/2022-09-20/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "af981f04545aca6d3fe301a22773ff38077e8c8d437b862a3d7f1e8040bfaebc",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "22fb1351c35e4d5b12d043cdf1de51a13176fc60518fa89226f3af9dc2e727b6",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "690a7880a563138bbd583b537ddb80bd738d8fceb4cab083bc8bbd1fa1ee2f99",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "2d5135344f76decf74633d95e2fc98c416093ca962cac608564abf600ff117bc",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "8f63623f1e0cae99c5d1c4bb1c636fb773ed06dc1d33a251c9253278f7bc1300",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "105123c48c7e0946872b8ca0fcc897c2a1fcccb9b71b1805eca01e713a509c0e",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "9c9ec824e1db607bfac14ce8a5d1e73aeb8670e655acb4577a8f6ee783202aeb",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-none.tar.gz": "b25fb3d1c41e193b724469898efe9d0f5d282de06d5224ae573c8870ccb5ed4d",
-    "dist/2022-09-20/rust-std-beta-aarch64-unknown-none.tar.xz": "4f87a8fc869f80072ad2a07896e50ee97b3412badeb69bb37f67ef47ff0d00d5",
-    "dist/2022-09-20/rust-std-beta-arm-linux-androideabi.tar.gz": "9ee992110e4bcf9a00bec8635cbe5bbeb241d2fb6b567060fa0507406708c8dd",
-    "dist/2022-09-20/rust-std-beta-arm-linux-androideabi.tar.xz": "aa5981a138a103843462a5a6987fcf0c7c335a5896517505d2e54fb288d7af1e",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "854d0d7c9932d4533d642e663ffa465741a3d0ec400a2c4b74324debaa0da27f",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "83b7b2dc823608777dc0e2f290fb5a2f8e6e35f4930ce2170309c14a54f136b3",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "9c84cc8ff79098be62011d572cd928faa4cf76c9c3e94060babc042073f3b7a1",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "b34a7bdd0ddac9e8b4ea8e1db8d86389c623a6629edbbd0052f890df75465fa6",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "49ddce546458f47683928fe341f5eacaec11c3c5a378ce8802c4b97425100905",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "e64130a854017b921a086749550fe92339cef0890ee645acbe23a30f5169a8ad",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "770bab36bd3bbc07739f0cc89c09689189edf7518e740f794be9aa7aed0917f5",
-    "dist/2022-09-20/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "b390f1111f5566b37d3f9384126a6d3dd0cdf9468dea19747394cdaae1c87c7c",
-    "dist/2022-09-20/rust-std-beta-armebv7r-none-eabi.tar.gz": "de22ffe26c7ef21d316933618b37c832353a7e5a1fb8b84af7bca98626fbc78e",
-    "dist/2022-09-20/rust-std-beta-armebv7r-none-eabi.tar.xz": "82b77c48fdb685b63bea0d40437d5dcee41500cec0360e393511b2c69d2c7381",
-    "dist/2022-09-20/rust-std-beta-armebv7r-none-eabihf.tar.gz": "94d427c65134e1e208f662acb3eb65a455016e3bed162bc4fcea9c6455e0474e",
-    "dist/2022-09-20/rust-std-beta-armebv7r-none-eabihf.tar.xz": "cdd71383ec150dbb2514a48300344a8547d7fee2c797e693cd7803354c5fca13",
-    "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "0241f8f9ffd00c02860158da46e20290d2ba563f93e8fa22324da2b87c347a0d",
-    "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "a780eb139416f2383f16fa63e72e56f0a77c67d2ac2f10f803802660e8ca30bc",
-    "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "1628e0c7a8cd8fb2b90db0e3d30f0d36768dbdeca640af967a14b9031d2d4c3f",
-    "dist/2022-09-20/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "4b06bbd8a0ae240bb19c93b54bd9da3bfe6c3f37a70d2b73dfcc9d1eff8fdaae",
-    "dist/2022-09-20/rust-std-beta-armv7-linux-androideabi.tar.gz": "9aaeffdd99e9dedb2bffa79adef2096ef29b48ffd2681ede2ea8d63179082f89",
-    "dist/2022-09-20/rust-std-beta-armv7-linux-androideabi.tar.xz": "d1d0ae4718eb43d4759cbe14a90b422edf0f451c8cb8624368800eff0f05c13e",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "5cf573ce6729200cc211924e45796a9aaf85001272a8690803944dbc91b5a2fb",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "51cecc72479f18c94620b2b022b89403e8ba519e36cb7a6f8c208a9ab6adb17c",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "c6e6962438e0a8ebdc7a7c74712d75642acfeb8c4b0753354a7d3b64da6948cc",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "4b812cfa63c380448b0e2ca2e03cfad73ba9951080eff1f76feaee25f67bdf39",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "89cdaf10019fc3ddb83ca1848adf8ac3411820a9095dc337b9a962f1e58be058",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "e21ac9106eae28b25270f6c1ce70e2c94273919c9c72d22f0fe3ad0b8f0a57f0",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "e218b7e7d148379411502023d6ede2831d76e29d5d2427d030f916a0c14c8ffa",
-    "dist/2022-09-20/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "2797698cf0f218c17eb22f43f806663723f9c89084fdc08d40adbbcde5a79f88",
-    "dist/2022-09-20/rust-std-beta-armv7a-none-eabi.tar.gz": "1d5c29d803cb8ef005baf44ca6a0b1fccc4227ab3585046f0d69cbf153be10d7",
-    "dist/2022-09-20/rust-std-beta-armv7a-none-eabi.tar.xz": "50259e9b9672baf54e176c252e9068cf020c4a79a825bafc5ee21fb46b9af815",
-    "dist/2022-09-20/rust-std-beta-armv7r-none-eabi.tar.gz": "805fc5ae72249f27ddbdd8afdc188b4f67dfe51822eb813e681259da51dbc75c",
-    "dist/2022-09-20/rust-std-beta-armv7r-none-eabi.tar.xz": "75151349dc9b6fd3b3a78d38827e517adc6935356bae0c5bc93bae62e1759db8",
-    "dist/2022-09-20/rust-std-beta-armv7r-none-eabihf.tar.gz": "ad4e0347c3e9b3f4936f26798ae2a8c502a4599c3357baf0b0a4cc3516c471bf",
-    "dist/2022-09-20/rust-std-beta-armv7r-none-eabihf.tar.xz": "4de3e5a729597473759b2db1f7e2ab633c98bb1d8125de6f458fe3bd0ee7d8c7",
-    "dist/2022-09-20/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "0b3c92539f3aec14501e09db3aa02854ef98fb4dc89721306798ea163041e669",
-    "dist/2022-09-20/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "26deacf11e62187673718ea8805b23dc2dd352efdedc11b396e117637a96ceae",
-    "dist/2022-09-20/rust-std-beta-i586-pc-windows-msvc.tar.gz": "a11ca7a7f67e225365423a704d7139d3c9193699493f1f193d579c126d492475",
-    "dist/2022-09-20/rust-std-beta-i586-pc-windows-msvc.tar.xz": "08d8aad1d608d2ff626f4e7e4300a31fc3f96c0ef1e5bd0ec179b98d4194fca9",
-    "dist/2022-09-20/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "3524e1ba92b9ccd1fcfd40a6018efa697d63177cc0a8c9cd016be833aff371f4",
-    "dist/2022-09-20/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "a6df83285ba5732eca9adf3f39d0a4087249b29cd0c33ee2272f68930df43191",
-    "dist/2022-09-20/rust-std-beta-i586-unknown-linux-musl.tar.gz": "6951d7aecd555a0dc485f57ad16703af65315c464aebcf73171bbda2273dba0a",
-    "dist/2022-09-20/rust-std-beta-i586-unknown-linux-musl.tar.xz": "f07c4a267740e46a4013b130e9d1e10492e45dc0226a08f5c909076ade466737",
-    "dist/2022-09-20/rust-std-beta-i686-linux-android.tar.gz": "dd47fbd29b3b025388352fafe693f25e43c1287f69c2185fecfb0d60e13a7fc3",
-    "dist/2022-09-20/rust-std-beta-i686-linux-android.tar.xz": "0eebd41330762a4bad438c40f134601e59a7b73043952b2e090a801adff41727",
-    "dist/2022-09-20/rust-std-beta-i686-pc-windows-gnu.tar.gz": "58cd47de74c201bfed62a8980c2447f97e7c129726d3d28c2140d880fa6d7975",
-    "dist/2022-09-20/rust-std-beta-i686-pc-windows-gnu.tar.xz": "7fd68f0f9eea4d8256132af2f2c269c58a278b757888e591716a553b87ffcf8c",
-    "dist/2022-09-20/rust-std-beta-i686-pc-windows-msvc.tar.gz": "816ef343a7ed908706d8f4e7cb915787a4e27c2390cc7c3f6e2210f3ad7c4cda",
-    "dist/2022-09-20/rust-std-beta-i686-pc-windows-msvc.tar.xz": "2475326f3d32e8ae309750c1639cdc6cce3474fb5d4820b31b46c9c12401b63e",
-    "dist/2022-09-20/rust-std-beta-i686-unknown-freebsd.tar.gz": "24a897b9916bcd4ad775d96f9751b06663eed599086d0665b83dd4c16af871ab",
-    "dist/2022-09-20/rust-std-beta-i686-unknown-freebsd.tar.xz": "959725ac2f49d1944c53846d920ab4e8769976d4025bc32bc63e5372b751a8de",
-    "dist/2022-09-20/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "35eb28ff2d3b383ac1b34bf6eded87f824ef93eb2c2d12c300b136c7c735ced7",
-    "dist/2022-09-20/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "31085015fbfa608e6d0828a367d84b48679c6a33d55ae32affe37307818b1086",
-    "dist/2022-09-20/rust-std-beta-i686-unknown-linux-musl.tar.gz": "ef294d01caeba013cc3173b5fab5daac4f0c64e57410f778f2891dff03f23875",
-    "dist/2022-09-20/rust-std-beta-i686-unknown-linux-musl.tar.xz": "4ad7915a9e54f7d911864adbc097941a9c051e0c97c8eee1c04158a5755e4e4a",
-    "dist/2022-09-20/rust-std-beta-mips-unknown-linux-gnu.tar.gz": "cd8e8fe2af17def5bf19a8a0993317af5c33833de850a489ef2dee54c61dbca7",
-    "dist/2022-09-20/rust-std-beta-mips-unknown-linux-gnu.tar.xz": "f2f95555e3564f7b16bcde9ae4c6a30752900519fba68304c4f74b7e508bacc3",
-    "dist/2022-09-20/rust-std-beta-mips-unknown-linux-musl.tar.gz": "7d42b6d7f028c637f7f9a2f0c14fde880e00098bf4141289620232a263fa8eb0",
-    "dist/2022-09-20/rust-std-beta-mips-unknown-linux-musl.tar.xz": "50dc97ba9ce28d4252f03f78e23bc05a702d5c1c5ad67b70a358406419f25721",
-    "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.gz": "7c14decb2404c616319c99415f65c1383264151f3802ffedfdff4962db310828",
-    "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.xz": "230849a3b6dff89671bf94c73391eee43107c81ff65c795eb1c9f30b9bb52176",
-    "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-muslabi64.tar.gz": "0a63c8f568d9ef12b75b9bcd53faf727bc029b4a1268c53fef913e58be94eff7",
-    "dist/2022-09-20/rust-std-beta-mips64-unknown-linux-muslabi64.tar.xz": "5b1020d65f651cd1778448618bca55906eef981842b73c18be1b5ec785d6bf06",
-    "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "3f747d83397942c88ad79a7bde1f98a57d0b416620f08ab57edb64f3b101f493",
-    "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "babdc0c87bdc8146fc6645da34776be98575019eacb95529c00060f8afcbb1f4",
-    "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.gz": "f0bc0b5fddf7ff8251fefa4068fdb623b47bbd1e81c2c732ce2304e4ce78bb20",
-    "dist/2022-09-20/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.xz": "33e3cc3766f941f3668a93240d627c7357b22a13facff5937821d92a21afe444",
-    "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-gnu.tar.gz": "33ae5fdfab257ddb004eb80ff1a8d0351675df06b97951d5c47071ac8b18ba9a",
-    "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-gnu.tar.xz": "9f16e21c54944afe83a5e1a3e489a2dcad947f367cbb17c6ffcfd2c503e03f25",
-    "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-musl.tar.gz": "065f8929bef2ff2ec0067c788fe64e0a08af0f5e11ac5d67e29e5225557d6d9a",
-    "dist/2022-09-20/rust-std-beta-mipsel-unknown-linux-musl.tar.xz": "c3c54ba9dfee447e33d76ba8060bb9b6081103fa6809ad77c3221ea064ed3fc8",
-    "dist/2022-09-20/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "5627e25a24792131e8c0a1c605908bd56ab5a55614b8e17c23233fdc14d25e81",
-    "dist/2022-09-20/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "3ee1b5228665f2d5d7ac500d6fa6a2d0fd771eaafb5c393419713a11bf8d0875",
-    "dist/2022-09-20/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "cbf922bba400e798cd32e5404a804400f79ed03ba5cc433173eac96ba9e06976",
-    "dist/2022-09-20/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "bc573b0012568f0bf397870660f7527697b1c65e1a7387d2419c6f63ba001bdd",
-    "dist/2022-09-20/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "917b948988edd4e5c5e441102f55b1541318d47c0cd5d958a69ddc6fbfda84d1",
-    "dist/2022-09-20/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "23e45711ce82b6baf9d27d909d8fe4bc6a5f32c91dfbc280708abfe5c362bc89",
-    "dist/2022-09-20/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "448710ce6f3e72d0831ae882fa37196685a28dacb6f0f499370fc2882427044f",
-    "dist/2022-09-20/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "831bc2bcb21ab9fe82fc64eb377d6d80b47198dc82677b7bd630d89819914e20",
-    "dist/2022-09-20/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "14a61cc3d6740a9033c6570264e6c9356120235f42e5ded8ebec1d592f17b47f",
-    "dist/2022-09-20/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "c73aa37081b4248324a459b48378b4a412a3561a12bad3ae064c8336ec862dd6",
-    "dist/2022-09-20/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "f78a367b9977471dae06fffd6049f03585e826fe648a2d1d8ee20ff19dfc913f",
-    "dist/2022-09-20/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "e8ef0dfd9d7df08fcdb7136619d8b9cd31435e0de130f87c799117dba9614a54",
-    "dist/2022-09-20/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "a7cd118c91f4de3a1e1c5d9326080bf39661f708c675272cf697ab9675001705",
-    "dist/2022-09-20/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "58023f2bd169455d97537aca228749e9a980e18d72f67ae8c0caff4ad2d4fd64",
-    "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "726efb0f7cbac9d13deac41d0937ce5f707d8e858b3bf9096c40fd41b2663d4a",
-    "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "73395ddaa22a91a1de120704f22d86b0624de52af773755046ec809b76b88954",
-    "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "2504e53a04ab1360309dd9e733798a94fa3a92cc575148073dc4870bb26367e9",
-    "dist/2022-09-20/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "f182ac3b6222a333d73f3f612fb31f9d19d4e03456e16b0967cb55d1292ec05a",
-    "dist/2022-09-20/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "9612105b0d40225d54f6b02bcf75e0d7d232331fa22a24de4895f97519dfd6a6",
-    "dist/2022-09-20/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "448b0cd5cc35096e0fd51a7cb1948e87b7c46eafe665dbfda802a4947e08665a",
-    "dist/2022-09-20/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "72459d840371f7203e15622a82ccb5e3559db52934445943fc41b11a58b07302",
-    "dist/2022-09-20/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "39ca5b49d6e909c81deb8c7391e01da9ef51cabea55fa55dc16cf654abb089a1",
-    "dist/2022-09-20/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "ff74af39ca1446f06f891beb22f1e24fb0b450d97c889dc2e27e0f53fc19b26f",
-    "dist/2022-09-20/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "f56aa4d390eb4b25aa51685ee773b589ebc87bbed7495a07d8af0d3b3cc7c715",
-    "dist/2022-09-20/rust-std-beta-sparcv9-sun-solaris.tar.gz": "df727ef09f0549051ff5b4fcd04cb22d4244cea85f97e43880e563c45b558cd6",
-    "dist/2022-09-20/rust-std-beta-sparcv9-sun-solaris.tar.xz": "f575d16acc0a8cc5b96e038d828ac023c302e38efed271d5101a885d3af35cfc",
-    "dist/2022-09-20/rust-std-beta-thumbv6m-none-eabi.tar.gz": "838be75f56d84d88ab01b0897d9b166b6ea26c527705df2d2a7368968439505f",
-    "dist/2022-09-20/rust-std-beta-thumbv6m-none-eabi.tar.xz": "b5b33e2de72e71ef5024fb50f0f6e91f32b4747f666aa7069e695b15119a1963",
-    "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabi.tar.gz": "dbaffcc17215c4342a40c049a9538be44837bcf86a7d65fb2c877f831beca337",
-    "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabi.tar.xz": "14fabdf3f4cda3bdb3ac139c95d31c2a20e3a88cd9f83e803f9c9bd6e3f9f83a",
-    "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "3acc833a086a9b46db81cbf03fcf2dc366a4b3d32eaeedcc2deee8ceea31449a",
-    "dist/2022-09-20/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "f6cf227b6da03855f1624bb325bd802b8ae6b15d7bbfbfbf8b9de1a74aedb6fe",
-    "dist/2022-09-20/rust-std-beta-thumbv7m-none-eabi.tar.gz": "e5054ca1e295709654b214847691f4fa9f031104725632dc056853382e74e733",
-    "dist/2022-09-20/rust-std-beta-thumbv7m-none-eabi.tar.xz": "b44f0462a5f44e762653fecd816dcd5ba5a2f9d8dd3efcec259b250f3af5237d",
-    "dist/2022-09-20/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "4fb8000ce077d346d85005343284f56ae936eac334c72cb8c170dbe810aad740",
-    "dist/2022-09-20/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "77955483882897980f4365bdc71fca9f39d675ff210519702b9cf3f2a6bcc2a8",
-    "dist/2022-09-20/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "566b9ea01182e6c7e3844911ed08fe5eb1d848c3de89d4f123d80fd70ff37ddd",
-    "dist/2022-09-20/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "33cc9c12d909bd4166be8570e27da8c1884ac1ce82d9548e16d926d9885ff62d",
-    "dist/2022-09-20/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "62a345ea2aa55b1a02053e7c49988c5f14d6b90d487b9c4916e40ad31957d1f5",
-    "dist/2022-09-20/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "37fd80af2188d837e870d3c8399767be15070344fc87d2b37b5126095252afc5",
-    "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "32a10d7eaaf2189d74700187fbc8c2ebde08b5efa06440c2b4f65fb85eac3ecc",
-    "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "dae8d6deecc5277be729803ed55dc134c1ef5bae49fa0fe7ea4f0eee9d7d19fb",
-    "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "44457f84d46ccd201771cdae9520caebf813d3283b08f73fabab59b52b485a98",
-    "dist/2022-09-20/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "1e5bcc82a403c1c4c5fa41fa78de9aa4378a2901e3170d9542eb3b21d130e36a",
-    "dist/2022-09-20/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "b7d31d0ec6fd1355d1323555ec8d1cb4b9f30bab32a75d0d8efaf465ab2aedcc",
-    "dist/2022-09-20/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "a25b5c180ca97d074d563c6fe1d826db23eba9212d12cb3d60d39b7841a5618d",
-    "dist/2022-09-20/rust-std-beta-wasm32-unknown-unknown.tar.gz": "31aeed83d12732c40e51cf59a3dd8dd1fe7ba2ed047cab65f8bfca8c72d158ed",
-    "dist/2022-09-20/rust-std-beta-wasm32-unknown-unknown.tar.xz": "28757fc2c5304b3110b2358d252fb4aaa8d811999bd9881e118bc71b0e6b01a3",
-    "dist/2022-09-20/rust-std-beta-wasm32-wasi.tar.gz": "b69ecad8480c2d33b854ba3387a0563df53546b8a2b55639fa20d1c104f35050",
-    "dist/2022-09-20/rust-std-beta-wasm32-wasi.tar.xz": "a09185a76891928cc65e4139373df6f22fd0060388ccc4530cc0be5310f8aaa7",
-    "dist/2022-09-20/rust-std-beta-x86_64-apple-darwin.tar.gz": "97f5a3dd01a7b5efe44662f2376826c184d212754c730bfaa21cd03235676142",
-    "dist/2022-09-20/rust-std-beta-x86_64-apple-darwin.tar.xz": "131b37e9d3c2335fb51427a7e0ab43362efccf90a4e001ef52e54aa221634eb8",
-    "dist/2022-09-20/rust-std-beta-x86_64-apple-ios.tar.gz": "d900fc396731c95a57d43519a109202cd2ed8e574df300cd6124c6390d1584a3",
-    "dist/2022-09-20/rust-std-beta-x86_64-apple-ios.tar.xz": "694dc51239481fe8f5ec2291b62435e0a7622591f2709ea4709749c7d8c01db3",
-    "dist/2022-09-20/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "c2fad6b25e33e4e52fab6d20b6e1bbf78f230e9b387f260b48f940bff67386f5",
-    "dist/2022-09-20/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "c3fe305a1082cbf1f5bbdaab5a94c0ad88037a4f99e556cf06ff1270a806f437",
-    "dist/2022-09-20/rust-std-beta-x86_64-fuchsia.tar.gz": "75b5aaf5206953d16a418c83f6e6d21a0adcbb0c81b5c1a8f467f3e5aa33c038",
-    "dist/2022-09-20/rust-std-beta-x86_64-fuchsia.tar.xz": "7fc38dbfb7833e9b6336f4aa38706a92e1922231ee875100e16274c571110757",
-    "dist/2022-09-20/rust-std-beta-x86_64-linux-android.tar.gz": "bc4a42ffc51bc3be27907a73de98fe4c8cf3b205fd1e7c75a9cb3bd30bcc5fbb",
-    "dist/2022-09-20/rust-std-beta-x86_64-linux-android.tar.xz": "68e02875090c7d382e8b21d0102712e7c9d583d657b6c51b4f939e3a9b7f884c",
-    "dist/2022-09-20/rust-std-beta-x86_64-pc-solaris.tar.gz": "11edbb1de67f00263f31635bdf006966143aa18423f574cb64ef966301a0fe3a",
-    "dist/2022-09-20/rust-std-beta-x86_64-pc-solaris.tar.xz": "44743e8f113e3e5bc7ce66902533f6ac59538f8f61fa6ab6c311bbf2ebe75e43",
-    "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "a705945830b23a25f272600470d687a1a9f5d4f8a01c5fed9e495a444b2aa9ee",
-    "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "9e4a045a2e0f754ec6afd897f1bcb66bccfc2b5bb91f141fa8d3d47723eb6090",
-    "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "910de4547c1112170b0981fecfc926cff4c47cc622648b83fea9f79c171cb05a",
-    "dist/2022-09-20/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "f9be82ac8c5f64c17cfbf0270b8f71ec71e6b3ba7c9521980a73d6f848f6014c",
-    "dist/2022-09-20/rust-std-beta-x86_64-sun-solaris.tar.gz": "e1097c0bd31baa61902be4a8a47a674faa15466ce9352213cf7808f50d5854ff",
-    "dist/2022-09-20/rust-std-beta-x86_64-sun-solaris.tar.xz": "82866dc52808549acd683d5b7c47fe97c992ea70cb6fff941007960a25f3b645",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "2b7c60a6b830d557a6df8865bc8dd658c84037a5893b11db8e11dadb527b5d6f",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "14787b285c55ab885d1360897849882eac861f36029cc72ec9819d035998ee9f",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-illumos.tar.gz": "4bfdf27eb12af6c4df132603a111ae26f4d06846af366a38e594e66c092373d5",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-illumos.tar.xz": "66b602046eaa83cf2b69bc75af4dd11dbc5d0c6878537d0af451d17121fdbbe9",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "78d12361e71630978928896a63e6cf7e3e866c09de761029b4e8e959850ac025",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "d55f9cb4a8c47fc5d0123cedf622b94b33f57a59022129e31f451e1b80f1815e",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "74bbecb0f35ad8a1aae65fb09ef21e38bfbe6d5b4c6b1d741832eb8f40eb4b1f",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "135f19e88ac0fb7ed02072c82a17f0d12abaf40055efd0a6b43bbbbc9c4445cd",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "0b3abb866d7b82bc5add9fa01d59b9223d2124d69dfd78a13a4dfcc17196f510",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "ec5989076c015e5b7d1a307ecb19f2ed12df7b5e2836d3b410f3743f9066683d",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "1e236c2ad7aa296aa389751ce64d1cecf86053d23126e1211da71674603e6900",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "de43876b381f7b91f7a8e1d1df5b92a3d60b22a62333f9a645f3e6055e91be3b",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-none.tar.gz": "568e95b842c3d8f9f733fc3b5b59a8c673d200b4d16d5db36ce24ee355e18c1d",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-none.tar.xz": "557727f83c7998b9349bb2c05f5ac5fcb2f0bef28e55f27b165fb7a2d9347396",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-redox.tar.gz": "e3c00ab70a0a69a9567bc525cc283318186521a6d57ccf4a42e2eb640ed50ce6",
-    "dist/2022-09-20/rust-std-beta-x86_64-unknown-redox.tar.xz": "757f2208edb49f23adf702a34b093551f0e193f6a6cd8c24b2cf4f199f77b2dd",
-    "dist/2022-09-20/rustc-beta-aarch64-apple-darwin.tar.gz": "a036bf0b4d0c8ab907ef2cb8cf8eacff41f7b82d519a2988a529c3d926539ee8",
-    "dist/2022-09-20/rustc-beta-aarch64-apple-darwin.tar.xz": "3c49210d4b867cefb4050507104b2672fc70e15f42615ced22469831b34b3267",
-    "dist/2022-09-20/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "21fefdaa0b70d7f4839751926ce102e19164a373e4d310c0f0b3655f3adbff47",
-    "dist/2022-09-20/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "7c88775b4efbb2416deb2b0d9ba86d5178d34059b18165b276658973f29d5971",
-    "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "99e30eb1612fd18b42a1b89602f448788ddcbbac2430577fc963a2c0c4708c55",
-    "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "385eaeb8f260a187ef828904e5267551062210543614dbf98d1c1e392853b913",
-    "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "8f3b377a74c586cc8f7cc165eac0794bec560e04c885ae552af4e5cf42490a1b",
-    "dist/2022-09-20/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "2fb2add792a9377cbe86aaedec389d023c35343ef801a97a2392f323e92c386f",
-    "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "658ec925f51b2a5da9ab8cb193c33c05cc294915d8c0c5a2e93f9ade383375df",
-    "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "75d19b64530691739654763b89468a7101457d638da04e25f549078594b67b68",
-    "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "c167fb9f352fed99d427094a5c2b96da0486f30ccb4188756def7c232083319a",
-    "dist/2022-09-20/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "ec1c19536b6fc3020ccfff727bddc934f89da0592797d49bf7149e96f7175451",
-    "dist/2022-09-20/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "05c5e668d5b40db5cd18b21192d8f0f1401d2304f715eff08ff49c3c97d740dc",
-    "dist/2022-09-20/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "ad620c5de3ae9a0b30b3c49dc89bfcd08a061da01f004a724ad5400efd4a7189",
-    "dist/2022-09-20/rustc-beta-i686-pc-windows-gnu.tar.gz": "2be9e0f5fe27b7085f65d32cb20875392bdcb177c582c58d1df842b316dcb9c5",
-    "dist/2022-09-20/rustc-beta-i686-pc-windows-gnu.tar.xz": "5841c0f4558d24ccd0c4e6996e399fe3ff13d5d1ffb2bda38bab6d60020fa649",
-    "dist/2022-09-20/rustc-beta-i686-pc-windows-msvc.tar.gz": "a19c5330c111ad4b19a724b22dc3381eb9f05a85bf9299dd13eefabbf6499504",
-    "dist/2022-09-20/rustc-beta-i686-pc-windows-msvc.tar.xz": "902811dec71e36989af12c8dc15b79759e5cf4e2250841bad2b9db2eb94195a1",
-    "dist/2022-09-20/rustc-beta-i686-unknown-linux-gnu.tar.gz": "36474bb89e67bc867cb7a4a5101d00be221d7a8b6a625535a5b2a74f505d5af4",
-    "dist/2022-09-20/rustc-beta-i686-unknown-linux-gnu.tar.xz": "4329562f89817b02e3eac219ad3051d9de5fab89e0678d91378c02a90fea7d59",
-    "dist/2022-09-20/rustc-beta-mips-unknown-linux-gnu.tar.gz": "bb7f5c8abc99d07a337eaeb3c1dc3861a4f148c364f58b039886f43abf6a7d01",
-    "dist/2022-09-20/rustc-beta-mips-unknown-linux-gnu.tar.xz": "0b02a4d1aac7c9d4b38fd760937020975e5de209ad23b7285cceae7992449d47",
-    "dist/2022-09-20/rustc-beta-mips64-unknown-linux-gnuabi64.tar.gz": "7ceae3da1fb1f865df3315ca450ec3cb4657086dd61c7a47879f98188aa38100",
-    "dist/2022-09-20/rustc-beta-mips64-unknown-linux-gnuabi64.tar.xz": "5a5273ed85d3012b8067dbc3e93f1af105e4cd80ed8055daade24f43dfb41977",
-    "dist/2022-09-20/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "dbd12a141765f29be2a602531db7f9a02bc32617635448f602befc90f1a574c3",
-    "dist/2022-09-20/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "e3abf34a09040149f8725ed1fca6e9c412c4cf290f9424541a819f0e2aa363b2",
-    "dist/2022-09-20/rustc-beta-mipsel-unknown-linux-gnu.tar.gz": "e13b7057525302dc3585a71461aa022ea0c059c0b0069fec44f86549eea94d18",
-    "dist/2022-09-20/rustc-beta-mipsel-unknown-linux-gnu.tar.xz": "b7bedce1d0ce44b4014e10122201c10443c0e8ced80084a6ebf1add1dbd3236f",
-    "dist/2022-09-20/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "fa738ae0d068e85044d2fce10f6a8bebe7b608630b9b7a822b2d7b84c6c59002",
-    "dist/2022-09-20/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "b53d773465368d9484cd36063d7ff202c1ca8d18422b4f6727cba54beb88f4bf",
-    "dist/2022-09-20/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "9fad6a7ae30e5ccb4f0779ffdd117f6cb30e6c5f6efd5247c208f9ee3296a27f",
-    "dist/2022-09-20/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "e8a92259aa371d350dc29171467c3e99fc178b636343ca82188c7b602a39ab58",
-    "dist/2022-09-20/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "5b64e0924a705e267177c8d80970f510487de350dda47cbc9bb758ec1b212a17",
-    "dist/2022-09-20/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "f6ca5a88f0ea25521e1135100cc7404547ffbbc4422b3c9a06177c94d7871ef4",
-    "dist/2022-09-20/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "8db27ac2a1b8322f529428ee7278347263a9ff71101d37bfb04056137f63de78",
-    "dist/2022-09-20/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "2475866ff4afe38755a27ffde9c09302066d0c936a4778883fee9a37c1b59f31",
-    "dist/2022-09-20/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "4cd81c652fd3e59cbdd42872f2e37bdcc1fa61a550eb8ffed7783e7ad3350577",
-    "dist/2022-09-20/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "1f8529e51192433d1a281484072f94d910ff81161efef230e6a2be82765f6f26",
-    "dist/2022-09-20/rustc-beta-x86_64-apple-darwin.tar.gz": "430311100511fe9fe176f01030b78fa8160840bf4d9b4ed798a2a7fe089b4f7c",
-    "dist/2022-09-20/rustc-beta-x86_64-apple-darwin.tar.xz": "57be1bb1dc7d7d0f0479d11e36d6315a9d19eb0102610b7f1dbd5151fe6ff5c2",
-    "dist/2022-09-20/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "826f3f6839c4e18e6d58a32de8f067bb57be2fb2c6cdf74f55d55ef76f5c5e21",
-    "dist/2022-09-20/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "5f8b9704071e6b372a5c67a29bcb9ba5978ffdedd62e057680aaba17dfc91ba1",
-    "dist/2022-09-20/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "06a29a85bfce9981504f1630c5f3ea86171948080a93d8dadb4a306dd678af80",
-    "dist/2022-09-20/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "36c2944aa3db18dfa1632c7b52c67e6bad9effb03960af8cbf82fdf32924019b",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-freebsd.tar.gz": "c20829efb9888d8097c9f5f472598b06868bf918a9d033d4b6fd031323878492",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-freebsd.tar.xz": "515b35360865016b7efe6f973730ce4c66021df0edeed8eb490b69f4bf50006d",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-illumos.tar.gz": "e63231ee48425687c97c654faba961a1b12379696459f284b6a4ea7ea52fb2af",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-illumos.tar.xz": "fae9048709741bcd21f4dc2ad8119576ba8dbe6b6442e79a443c207a4c52cc47",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "3785a8837c6fdf230b79992e4a3fd6a8b6faa269461bf908e427ffbd59728adb",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "a238209d54c2f9fea99a18bf43c4c0ae9bbc9cb10075e63d77af131728d64892",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "520ba16bf1b892f5c3d3fd6c1ba695ff48e0babd4ed5be97615887589e60c204",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "4251abe5dde29e9d2ffd7560e7f8eeb5c1b4ad6078b27896f63fcad5db6dabeb",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-netbsd.tar.gz": "ccdde196a376d8f06d3457a1b6d85b4b3692acc9e4bd055fb93dcb217ecd4494",
-    "dist/2022-09-20/rustc-beta-x86_64-unknown-netbsd.tar.xz": "22f5defadc7b4b4231315b420b6ee102c188a03381580feb7e22b75e16661017",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "8fead022ff05d4112e4cf7e637a459651dc793d9c38f1e823437f6c0c1bf6791",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "5e5f06c2c7a0567bb096b676ecde4bc87cd56c1a60d5e99feb0ac0b679280e1a",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "f365e910e58e962526bc2ffc01b47ea7b99b2be199baeed82e3bb0609147994b",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "3e8a10a285b3c248691e20090d2805d0aabdfc0555a5463bc472899fba085759",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "4316414f83c30535f0c46efba5fd011755f4afa6cc3440b39e8ec154ae451b69",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "f3bf1d0198db6efb751fd61d096615d09dec94a2732b028728d74a3513e9bc47",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "d0f4c30d1ed5144ce0a2931290cb730efa5616375ff846692faba0f04b2fed4a",
-    "dist/2022-09-20/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "3c20a791400f994ea5ae681700e9bd1773b9203821a5458448a038b70fe98794",
-    "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "587eabfb4ab41ec7fc1f344f8d8674feb1787cb402dbc10754c43eb9352233f6",
-    "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "0f5c05ce846c42f4afd9127fa5bc0af070e7a03911ed93630177d6304ec66fe9",
-    "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "dfcab7b0d9b93e1ce639a7a1b9774a41e1e70b67fb91814393531476e7ff6d97",
-    "dist/2022-09-20/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "aa66edb8208b73e3dc939ce82afb78b9104022427fc2278a50ca004c54b1fd5e",
-    "dist/2022-09-20/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "9d31be70cf5aa67219bf85e303651b928e89f54831a14ad004ef606291206991",
-    "dist/2022-09-20/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "8b1249489bbf015af1865ddeb83560fbbc52ca84937e14a2ae928eb4fa854322",
-    "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "4154363e7fc7888088ba020c7454a2e0ea75a64e01532ccc709dba3a16c48d78",
-    "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "48ba35bdfe87b78428fde9b7ff6fbd7682d7f94113b874e8308bd3c5e734154c",
-    "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "bf42294f1c3053b8ff6dcf13219056a5e83fb0680e5e53621c626f825f2f11c6",
-    "dist/2022-09-20/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "a08b27ac0b47af60618f07d884320e80665eab550536e19828b5fe139a59499d",
-    "dist/2022-09-20/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "748c42c63c3363d820b132a96bd95cd5203c5f808fb4885710065b9c609ae183",
-    "dist/2022-09-20/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "a2bfe3e12d0ecb5881a749a3d11652d45efcd9ee3647ea7c6b6cbc94071e34e4",
-    "dist/2022-09-20/rustfmt-nightly-mips-unknown-linux-gnu.tar.gz": "a26f46c9d777ca55db8ef595219aea45c3ff7547ee7cfe07d01b9535dc00e1dd",
-    "dist/2022-09-20/rustfmt-nightly-mips-unknown-linux-gnu.tar.xz": "d3ba043430fb0177023d4028c90a797d9b469d4c2fb2c539bb52e2dd070723cb",
-    "dist/2022-09-20/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.gz": "1f7b83d238dcd63de150d5fe457b8c04347626a583f049cac7989644787c2432",
-    "dist/2022-09-20/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.xz": "25f682b00e0362c16cb8d9879854d0a9781dd7a1e0f980a0c5064fad3764e8ef",
-    "dist/2022-09-20/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.gz": "09aaccf92485ad1d69623410f947b264835236d20472f974e348015bb8d2353f",
-    "dist/2022-09-20/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.xz": "cff865ff4979885f158af7b437ebe67ea2645489a6067abd97eeaa97b57041d8",
-    "dist/2022-09-20/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.gz": "1ca400758f5e44fa8ed01d8fc6d5622259cde42597fe80dc5e0e1b4129270c77",
-    "dist/2022-09-20/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.xz": "bd71382043ea4e934640a60660169d8785af453415d88945066bd6b8a42b0099",
-    "dist/2022-09-20/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "86feeca2cb543476419583bc1b10dbf8d91afd25ac77a9f04f789aff4f34e3e6",
-    "dist/2022-09-20/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "2ce655da2899200f9e4a331a45f005f4faea11cfdf5b74397a68d376dab88bf9",
-    "dist/2022-09-20/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "ed7e14c85bb1dd2900991775e812c56a76891a70dbea013bff06f73e7d1beaba",
-    "dist/2022-09-20/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "09710d4622fd7a9695d3907be6433c69f2ff590415fab776a05ba74ea5be63a9",
-    "dist/2022-09-20/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "02da320a5aac0eaffa83cd49b37d0cbcecab7686cef166f30dd5ed02fa9cb023",
-    "dist/2022-09-20/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "1f4465079a5493ec23f433cae4af3f633885b38f6ba7693387ccd355010b955e",
-    "dist/2022-09-20/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "a9a3bdba43897b0ab44c54537ce80fc969c378480348af715e293403b55b83be",
-    "dist/2022-09-20/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "2d58863182dac199cde47e12d3d92a64b5d068afc7e97e1047ae8b369fbe6df9",
-    "dist/2022-09-20/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "002667d802f1ff1224318c2090caaa3eefd18dabd1bcd40ad957b0c09acd047a",
-    "dist/2022-09-20/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "a34aa6451cf5faf985967ec5df78a41b21ae317bba7f8836e09f87571a7f9c16",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "8950773a8433fcc68f16c1e07210c3cf07c9e9240df92b0a90c67ca285d932f8",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "1ce5bb4a0a3490f95037a1512f4b5539c65bfdc5d7f6d42005019345bedb71e8",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "cdbef502285d5eec5788299354f391a2263858d630466cd1cc6d48748aefb1af",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "d5e04d0abda5e8ec429794a58f6404a6b95cc66cb218a52299c3bfaf1ec35485",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "d1ec51e70918e4f50463839eb86528582424d69eecc6af5cd07987621acc713e",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "64a108e60c1ff0e1c004d63e13a8180e5b266382e8434aa94eaff3c654158e51",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "d439ce2780177b3123de9a4c6727ea19831a215d19363d12b0bcc3bc19fc5074",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "851adf8da102068b4195a78ba587e168b7c4471b5a2e66451385a503362d091d",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "7bad988696f60f49c0ab1929f8cfe843effaa455ab1d20b002dadd1e10bc4ded",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "8bee32774f9af0b6f382640592b65d4187401a59bd3bea4a139e2dc43471bc0c",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "16a3ff01a6bc471da2195c7e8d0a6623b911d956db388b66e39096a7c81ae1d4",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "721115ea0ec7902f74d203eaadc71c79c4489caf9a23b0a81c513fddce5fb9b2",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "9042aba5f18ef575cff2e106c34b707d8fe013f4140e4a066ce80f2103563809",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "9106ee07a9f173ae845ae2b5ce30798e15deffd46149430ec5aacceaed7848b8",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "9acb78526c40efdfa0087373d802b30b75238e4c46e0bb18262e16416be49b4b",
-    "dist/2022-09-20/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "c7a73c5b9034417998800dabb08ba71e12713a299457381a4437ea454cead1bd"
+    "dist/2022-11-01/cargo-beta-aarch64-apple-darwin.tar.gz": "ebc0b11a2af0385bf11a5183dc159d890161be45d231acc34c6326aa25b84b95",
+    "dist/2022-11-01/cargo-beta-aarch64-apple-darwin.tar.xz": "a0e44bf77337518e2200c34cb297a91dd4db51f0d331ca4cc496989da61676b3",
+    "dist/2022-11-01/cargo-beta-aarch64-pc-windows-msvc.tar.gz": "a4beae1c53df4d35fe991ebc713e37246d4d89e5543ec740274605a7124806b3",
+    "dist/2022-11-01/cargo-beta-aarch64-pc-windows-msvc.tar.xz": "5f8ec5c8b012d7e6bc28ca3d700c1c7c742f6532adb044539cee3b2280c1056c",
+    "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-gnu.tar.gz": "54d8fc5ce70b1f06164e17e34d33abde7260c6b1f3356d98d77271ec89766fb1",
+    "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-gnu.tar.xz": "f2debb6ae264fefc49380997759bb0b5022ac1c65ced9bc17bc146671be37116",
+    "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-musl.tar.gz": "7a8e10be17c8cd624fb3ae2bb7eaab3c493b637c2c1c1100b5333982d1dfd962",
+    "dist/2022-11-01/cargo-beta-aarch64-unknown-linux-musl.tar.xz": "553decfc64b56d9967ae067bc942ef7117c81d6976b5fa4cf8e5171397836af7",
+    "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabi.tar.gz": "64bdb603cdc05b983393d707e9e6e6cd1c71dd8213d08b3d0d1cdf168ceb165b",
+    "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabi.tar.xz": "0afe4ca54c65668257dcad5941c678498ab917bbf82a808f39c093719a53f2ed",
+    "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabihf.tar.gz": "c7fe3bacc9c4acb9b42677281655904b5ed5aec27042b9a8cf9743b737b6b657",
+    "dist/2022-11-01/cargo-beta-arm-unknown-linux-gnueabihf.tar.xz": "57f985ccaa2452778c90733e2586a991969dc15697bdbc9547da8a62c871b674",
+    "dist/2022-11-01/cargo-beta-armv7-unknown-linux-gnueabihf.tar.gz": "873b2a0c2990eef29d689984293394e6972b4659bd6e4c31fb9bc9c8f1c679f9",
+    "dist/2022-11-01/cargo-beta-armv7-unknown-linux-gnueabihf.tar.xz": "f8a9e74159594d57ce8dda1f7ce7ee4e1d494b9135a0f32b3afc89a637cad8ae",
+    "dist/2022-11-01/cargo-beta-i686-pc-windows-gnu.tar.gz": "9570141b118c2339237aac12c1e6d71c138ccef784db2effdfd9d02fb12d0d0d",
+    "dist/2022-11-01/cargo-beta-i686-pc-windows-gnu.tar.xz": "183b63cded6c4cc26feaa14be036a619289b155a6718f4964f94c38a9208742b",
+    "dist/2022-11-01/cargo-beta-i686-pc-windows-msvc.tar.gz": "9382bf364c5fc9400fb22b046c0a951001961efac221f5cd0f9bf45b1005d36e",
+    "dist/2022-11-01/cargo-beta-i686-pc-windows-msvc.tar.xz": "aae0a58b9711365ce1d76966af7387f310b496859a9e02ddbff8e23da93226c7",
+    "dist/2022-11-01/cargo-beta-i686-unknown-linux-gnu.tar.gz": "507727f9b5a920ea28e7104c9aae681c50fa8aaea446a3e10b991a9408adaefc",
+    "dist/2022-11-01/cargo-beta-i686-unknown-linux-gnu.tar.xz": "4ebfaf11ffc346eec9f05b2d93123483b784b83a322cca6f5fd406066ecf0fcc",
+    "dist/2022-11-01/cargo-beta-mips-unknown-linux-gnu.tar.gz": "6407889854bee2e45a00585abb4fc8b387103e33e3e67244dba4e140abe46480",
+    "dist/2022-11-01/cargo-beta-mips-unknown-linux-gnu.tar.xz": "1aeba894f0ca756dd9c3d9b99c7c94bf1f49d5d87ea919249fd0fcf195eb9c52",
+    "dist/2022-11-01/cargo-beta-mips64-unknown-linux-gnuabi64.tar.gz": "292a95a8de3387832173d9adde633b3d34a019879f97bf196cb41556c3909337",
+    "dist/2022-11-01/cargo-beta-mips64-unknown-linux-gnuabi64.tar.xz": "872819f00ab0a848401d7dfbb18cf139f85b3d8e48eee0a034cf7f0b970bd865",
+    "dist/2022-11-01/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "15eb49c334688e48e83f2565c620b3f1af29775599406efa1814c78ee80673cc",
+    "dist/2022-11-01/cargo-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "e74f6884e71109d36d03f7147b7e506f374ba291aadbe4246f6c429bd6fffd1f",
+    "dist/2022-11-01/cargo-beta-mipsel-unknown-linux-gnu.tar.gz": "7f3cf8b35465e4df5fc18cc7cb4f4db6e1b240a39f7583126d7f8ad6d18e8bf0",
+    "dist/2022-11-01/cargo-beta-mipsel-unknown-linux-gnu.tar.xz": "c59f2893999dd88a55c0a5bdb4436640ae9c18f943baf48f63eff6069f7a3e8d",
+    "dist/2022-11-01/cargo-beta-powerpc-unknown-linux-gnu.tar.gz": "566c315b6206a63bf33acf178547bb757a8803e3cfc71f1f63ee033eb6a17138",
+    "dist/2022-11-01/cargo-beta-powerpc-unknown-linux-gnu.tar.xz": "814a8e8f8f5caf5bb4018e54ffc2c1bd9d23df94dcaffbc04881b91bb3c8aefe",
+    "dist/2022-11-01/cargo-beta-powerpc64-unknown-linux-gnu.tar.gz": "5db8a63532be5fb9511238d7976075496aba6c732302dcc27bed9ae61188f917",
+    "dist/2022-11-01/cargo-beta-powerpc64-unknown-linux-gnu.tar.xz": "6b95c8cc4dda3847f53fb62ea711ca99c1b1b1639249b8b01d54a9ecbc4421ec",
+    "dist/2022-11-01/cargo-beta-powerpc64le-unknown-linux-gnu.tar.gz": "2ad497be28760f7e4ec6dfa6421a6c10ab049e0dbf45ecb3a2dbde5db7a959de",
+    "dist/2022-11-01/cargo-beta-powerpc64le-unknown-linux-gnu.tar.xz": "6e9b982857c64518c10392779528e7065191262a95e091ee289c8668b6cbfc4c",
+    "dist/2022-11-01/cargo-beta-riscv64gc-unknown-linux-gnu.tar.gz": "88a0751ef36816f9e26e9f6d72809687b1f6821b32a3a17c58feaa32f882aecf",
+    "dist/2022-11-01/cargo-beta-riscv64gc-unknown-linux-gnu.tar.xz": "bd6f626002a0c5a3af975419a1258a77c9db91e0db5d4acccbc7dbf25ffd17c8",
+    "dist/2022-11-01/cargo-beta-s390x-unknown-linux-gnu.tar.gz": "69bad5758f27f53d3e48abcd5aa70b16eb29d5445233c65ab50a8ad0a1629077",
+    "dist/2022-11-01/cargo-beta-s390x-unknown-linux-gnu.tar.xz": "06212f4cb605fb79d811060d3096bc4b43cf00e1a4fe4a375154b56ff60c92f5",
+    "dist/2022-11-01/cargo-beta-x86_64-apple-darwin.tar.gz": "741f3490b5562afd57cdda846ab322c69e20940bcc11f3ca5690d662d5de280b",
+    "dist/2022-11-01/cargo-beta-x86_64-apple-darwin.tar.xz": "2d698df7c00b7c227ca388830732a8787b2a85b328b554c0f8c417813d97ef46",
+    "dist/2022-11-01/cargo-beta-x86_64-pc-windows-gnu.tar.gz": "9c22b476f25c3f0946cb834da3904516248137cf22c5eed30432401ff061a4cf",
+    "dist/2022-11-01/cargo-beta-x86_64-pc-windows-gnu.tar.xz": "1604c5d60379227d26d819bd2f7a57c79a9e000a6077ec06e95b418bb0351180",
+    "dist/2022-11-01/cargo-beta-x86_64-pc-windows-msvc.tar.gz": "673d8941202c2113a431fcef396e604d7ea79000c97a64ef6e93b26956f75fe7",
+    "dist/2022-11-01/cargo-beta-x86_64-pc-windows-msvc.tar.xz": "3d613d04b48a2eb8644e2bfbb07a88cefe02c7b5cc7bf061b8ef307980230d47",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-freebsd.tar.gz": "e0ce6fa69af565e3b79f7059a4de88e39955d7ea6866d56c2b0946b47929192f",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-freebsd.tar.xz": "de602b7802b1448a861df05c41430dcde4f07358a05711784a1ca37836525b74",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-illumos.tar.gz": "c4eacf4821c126b321a67e0233d2f84571b3dcf25686165cad00d9645787f03d",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-illumos.tar.xz": "01ec5ab637010498b784ea2fe6aacea626fc341792eaa5a50756f9b483a765e5",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-gnu.tar.gz": "2e6efadbcf138ab72750c1375bfeaf2d5102559aa9b745294b9973821e193703",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-gnu.tar.xz": "e089b1b4248ad8e05ba54cfb278101a74aa34154bd2d44dd50119026bf436d1d",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-musl.tar.gz": "ca079fce260accce11c1fb27e550421cd0900027e29b18e24e54a298d78031c3",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-linux-musl.tar.xz": "ff33e9fd6f06e02277f580f13d82f753987f4dad7d7926405b63dcb362eec498",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-netbsd.tar.gz": "46101fc5f53595ae53f3ceb755cc72c078471479a337b5319c85e629e5df3b28",
+    "dist/2022-11-01/cargo-beta-x86_64-unknown-netbsd.tar.xz": "b063425ccc69284e8788211bbde5a7843bd16a3b9c779fab68a11d22ebdf319b",
+    "dist/2022-11-01/rust-std-beta-aarch64-apple-darwin.tar.gz": "77bb5db904089e087032c24fa2e011536e13d3982299285a7515beb97f445078",
+    "dist/2022-11-01/rust-std-beta-aarch64-apple-darwin.tar.xz": "63aae4b9f10f15fb48b2ac20aa7f112a685d49bdf94d8997d036472e928fcbde",
+    "dist/2022-11-01/rust-std-beta-aarch64-apple-ios-sim.tar.gz": "8f63b6be668e6a25411582db9145c9de8192d58acb42c490b0de89489a3e36c6",
+    "dist/2022-11-01/rust-std-beta-aarch64-apple-ios-sim.tar.xz": "d862bdeaf2c78b15babaf74cf1c6feaa5c4871a90095f3d4239d81f44217cff4",
+    "dist/2022-11-01/rust-std-beta-aarch64-apple-ios.tar.gz": "e5b1e9420d387a1442c77bed10efebd7b0268713820a728a067bb4ead6088041",
+    "dist/2022-11-01/rust-std-beta-aarch64-apple-ios.tar.xz": "569c667e422ca7ac373d59b6e13c299cdb7f334164c84e6f0c8d0f076352fbf0",
+    "dist/2022-11-01/rust-std-beta-aarch64-fuchsia.tar.gz": "3f945c43c09704b3df6af66a2132da12243b13752094383965d6a8a83c6edb0a",
+    "dist/2022-11-01/rust-std-beta-aarch64-fuchsia.tar.xz": "3662f02892ab184be99f93a9d0f99e030a73cc61447934b74fcba84e05b022b1",
+    "dist/2022-11-01/rust-std-beta-aarch64-linux-android.tar.gz": "ab04a0228074e974d70a15e594d57479fe22ed37c8acfa5104201dbbe57747a7",
+    "dist/2022-11-01/rust-std-beta-aarch64-linux-android.tar.xz": "fb96925878a24dc9e90d356e96cf4fd1fc9152c39f8914f9a9bb676d78069cba",
+    "dist/2022-11-01/rust-std-beta-aarch64-pc-windows-msvc.tar.gz": "45e824f75ac530ee9eaf0b0a01cacd5b8dd64ddf5203c032c49fd2bc4fabb245",
+    "dist/2022-11-01/rust-std-beta-aarch64-pc-windows-msvc.tar.xz": "a6488faf4c87cabb4467f4cbe7348d553045c2f10f450bc6e000fcf18ca9b073",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-gnu.tar.gz": "e2a66e04b24aad8a8898d6c0270d8dcff63205213cea3b893807ef186e8c0936",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-gnu.tar.xz": "a4244ac1600726b5abe6b5f9a171fc2e4cc57bbe7cecdeaf23b69e906f05e303",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-musl.tar.gz": "9e3e0f675ca50b7a2a1afeacdaf5d7f2f4ec1536f596ff99aadacfcb59fd42f5",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-linux-musl.tar.xz": "7e7a8fb4fe0283b71deb79c5ccb1ae61b2099392b3c8e09d03d4a68fbab7a184",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-none-softfloat.tar.gz": "7543df1d71d805b079d19ccd785f777918b3f11b131bca05d079cb5d3952a38b",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-none-softfloat.tar.xz": "eb082e894047cd77ac3fcc9c03eaaef77e6bafbd075cb0d62ba3a3ba277f5d64",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-none.tar.gz": "144fc6973b06ffb12b5ad0bbfc9fcdcb2a0732de50bb140d62d6af3d6b462908",
+    "dist/2022-11-01/rust-std-beta-aarch64-unknown-none.tar.xz": "8ee2ba2d4eca35a426fb089e0f0b50b2ac3ad1ab036c5f8f4786e2953405092f",
+    "dist/2022-11-01/rust-std-beta-arm-linux-androideabi.tar.gz": "4a46d6591c1983d0853f7596f7b76e7c82b6b0cbfd97802b565a17aece0d13be",
+    "dist/2022-11-01/rust-std-beta-arm-linux-androideabi.tar.xz": "3888fe036b5fa9a5dfa009462a002a05c70e56eb70db3a0c872fab1432e9c9ed",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabi.tar.gz": "529d668389506443f87bd93e98dc72d12be9a4ab41675dc6a1c7373e934ca017",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabi.tar.xz": "dffa1a94f4166435d6fe2a76a4d35deb8c128cc93146f181979416816e77e29a",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabihf.tar.gz": "2913bc06d6b49c52804a8dc18d1d3cb1b564e0272cba93f8594747731d360f9c",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-gnueabihf.tar.xz": "c12bb97fcbeeb0a9a71b2575b2d5113948c515616f720dae3891e2aa886d03a7",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabi.tar.gz": "a844ad8a80fa07b9196dc040d5171749daf94443c57348bca04e69b8dad37cba",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabi.tar.xz": "c261662fa988748ed03722d6034228c893e02a0e473f906bba61c1f43be7cd79",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabihf.tar.gz": "1ae5967f4fb80047711519dafea21fed8d6afd308566033e468c11587073d216",
+    "dist/2022-11-01/rust-std-beta-arm-unknown-linux-musleabihf.tar.xz": "150393cde94d8349eb488a161572589c53fed456c8401e5b1a59d1dd87003f7c",
+    "dist/2022-11-01/rust-std-beta-armebv7r-none-eabi.tar.gz": "b1777a389e4db0ccd80ece774865bc99731c4b483be80c909f1b5a2a185dc5a1",
+    "dist/2022-11-01/rust-std-beta-armebv7r-none-eabi.tar.xz": "877a00491650bac92e93760c2457b644d2b5ee28d410c1e29fc4b40c05da493a",
+    "dist/2022-11-01/rust-std-beta-armebv7r-none-eabihf.tar.gz": "3dfbf001db319a41874e2c0de2f55407285d88156fa0563cfe3c3bb1939998fb",
+    "dist/2022-11-01/rust-std-beta-armebv7r-none-eabihf.tar.xz": "b2d6a543cdf64a5c147001ea30d07bd13b98e2918a343bff08bb57eed1f81462",
+    "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.gz": "cd8f0803ef86052d09606601b09dde05d1997a93fad7a22604fda1176157040e",
+    "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-gnueabi.tar.xz": "748cef6595fcd30da6735c29476639ac80cba94eb627d6654665d656da2979ec",
+    "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-musleabi.tar.gz": "1dcae3588a3e552778ff1079a92750bee15835f08f8b9ff1123e4e6c5a73c087",
+    "dist/2022-11-01/rust-std-beta-armv5te-unknown-linux-musleabi.tar.xz": "3711105029d28fd91f413f488b7041ea42c70e5a244f992e9259b4e9d52abed1",
+    "dist/2022-11-01/rust-std-beta-armv7-linux-androideabi.tar.gz": "a2af3f6d3681e1c545d0c21bf04fbfe3de1cdb2273fadcbbb4408f5590054d11",
+    "dist/2022-11-01/rust-std-beta-armv7-linux-androideabi.tar.xz": "23e658070e1cbe8011d48678f57bedbbde819cd64f43509858af563a7073a3fd",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabi.tar.gz": "ff7b429d5a6d33f0e467b333225f7c42de279ccf3e91f3ef7c5463dc06939579",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabi.tar.xz": "8d41b293656c5cf93f46754499e5723a89dd997d3723bfbe56f953a7d864c435",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.gz": "6dd89ed0f20a0ea4a279dd4f810c7908c3e8a377da8a2983f8890efeea169177",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-gnueabihf.tar.xz": "504fb533fca6c46ad98c728781ab31170d65e5b35cbc9199aab97b1146a24702",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabi.tar.gz": "f9a731fd3ea961f0c5eff24e6290aed19d79d5444bf562670abc0cd46ee309fe",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabi.tar.xz": "825acb16e4bbba0c9b535e635b972ec581fe6ef115c5a41bace9b85c704eccad",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabihf.tar.gz": "41da8404f0e3cef386f6efef9b27fde27de77de71140dceeaddd8e15260ce45d",
+    "dist/2022-11-01/rust-std-beta-armv7-unknown-linux-musleabihf.tar.xz": "c8f81fa9cfb40ce92f2c95ef8b57e8a62d819628111e1dfe0c6760fb48802be3",
+    "dist/2022-11-01/rust-std-beta-armv7a-none-eabi.tar.gz": "fa8c3168dff5c167c6ed25f9c605941ab51e73e70c0dd162a5fd17287c5fd5a5",
+    "dist/2022-11-01/rust-std-beta-armv7a-none-eabi.tar.xz": "36a5ff7865f8a16b867ab3fff4ac32f0c62c260a5c27385098e67b75b21790fb",
+    "dist/2022-11-01/rust-std-beta-armv7r-none-eabi.tar.gz": "0417cef6468fd66bf626729e7c0089b47b149cfc43e8e0d4281f76f73ed17edc",
+    "dist/2022-11-01/rust-std-beta-armv7r-none-eabi.tar.xz": "1de6cb38a68ef336e1edf2c1c51d999482898df99e2bc078cafe6ac5380bf3f2",
+    "dist/2022-11-01/rust-std-beta-armv7r-none-eabihf.tar.gz": "91003d4648fb01306d6e0a0214e089d444a57c5ff09138040f07cc81e89af639",
+    "dist/2022-11-01/rust-std-beta-armv7r-none-eabihf.tar.xz": "884306ac77518ece0cb2f22d898e3d2aa50698bd4181ca23a1dada6d82778682",
+    "dist/2022-11-01/rust-std-beta-asmjs-unknown-emscripten.tar.gz": "f17ca8f54eca5d73006659fd08142d537eff23731b6e5a35bd67efafe0dc8cb1",
+    "dist/2022-11-01/rust-std-beta-asmjs-unknown-emscripten.tar.xz": "b04a17d33d7b9b1caae666dfa5ee9a98e5dc079773b6345f6c49733731e14bfe",
+    "dist/2022-11-01/rust-std-beta-i586-pc-windows-msvc.tar.gz": "55e61aa74bdb50df54394a0f62b9edc88682c37b51fe9d8d5c05c0619eacd1e3",
+    "dist/2022-11-01/rust-std-beta-i586-pc-windows-msvc.tar.xz": "ec3d887742289ef9c171ae56ca20c3e9cf1972cc3e6c511611404070c55dac8a",
+    "dist/2022-11-01/rust-std-beta-i586-unknown-linux-gnu.tar.gz": "a36444f0ba0e7e03d06fbf65d830cb7067c675ed061e8f6efd6ed445d5955e88",
+    "dist/2022-11-01/rust-std-beta-i586-unknown-linux-gnu.tar.xz": "dfc07297ee8cb63f76d2019ae822352e6b42e5cccd225eaa5597a63ecff3624f",
+    "dist/2022-11-01/rust-std-beta-i586-unknown-linux-musl.tar.gz": "e8de9f830cf277be584b54d86d6621a249fb2987fdf32d5f16cde9b492722d45",
+    "dist/2022-11-01/rust-std-beta-i586-unknown-linux-musl.tar.xz": "f9d8bd74788e2209ecb8d0cc49d94b4e2752c9239f89bcdff3e8fae315d1d923",
+    "dist/2022-11-01/rust-std-beta-i686-linux-android.tar.gz": "b15636654925fdba1e9ec1704573e4af1fc5f1158a0657b245901e22c06cd378",
+    "dist/2022-11-01/rust-std-beta-i686-linux-android.tar.xz": "9abbfcaa40d86e8a4cf49f2a58b1c7b2f422b6890303cb43feb83cfb8f650a42",
+    "dist/2022-11-01/rust-std-beta-i686-pc-windows-gnu.tar.gz": "30953eb457a397966221dad058ff7ebd99ca4497f184016b5a61db0f122bdee9",
+    "dist/2022-11-01/rust-std-beta-i686-pc-windows-gnu.tar.xz": "f9d6d266eb3bb46c058615786483d817138aa29efc3c62c3cd9c87e572956b12",
+    "dist/2022-11-01/rust-std-beta-i686-pc-windows-msvc.tar.gz": "b55202c349a4e9a493a2de7a3d48788befce32274998d3dfc1d1b6f4a96ba9e3",
+    "dist/2022-11-01/rust-std-beta-i686-pc-windows-msvc.tar.xz": "6dd8d42e5712d699704e85bb90cd42e0142a4fab7cf7f80132cb0902cc415ccb",
+    "dist/2022-11-01/rust-std-beta-i686-unknown-freebsd.tar.gz": "d2a7c9e7f1dba3a317692a46f8efec8d7ba1e9e943c88d3f342a820c34829aa0",
+    "dist/2022-11-01/rust-std-beta-i686-unknown-freebsd.tar.xz": "ecf6abb631dd6887b5630d1ea0b8778fc1539405e6c00d7585c8afa2230ef9ec",
+    "dist/2022-11-01/rust-std-beta-i686-unknown-linux-gnu.tar.gz": "93c5912258a49a003a12ca01101f5935d5894f9a133301a47047cca934a7439e",
+    "dist/2022-11-01/rust-std-beta-i686-unknown-linux-gnu.tar.xz": "f8a6e67723cb968e874827a6148a5e25d3d45c56577faee627010347d0f03d92",
+    "dist/2022-11-01/rust-std-beta-i686-unknown-linux-musl.tar.gz": "8838592167a8d68f463dc18e55d5d2d55c474426e8a4ec0f28fd2cd4230cf638",
+    "dist/2022-11-01/rust-std-beta-i686-unknown-linux-musl.tar.xz": "c8330a06862a7f375b57774b382a54a1280c33ddc1b94d5d5ec45eb6ff0de8cb",
+    "dist/2022-11-01/rust-std-beta-mips-unknown-linux-gnu.tar.gz": "4b50cc174eb1da9dc831de828e6ee2fc8a81abf8e6dd52b041e4ab00eaff73ac",
+    "dist/2022-11-01/rust-std-beta-mips-unknown-linux-gnu.tar.xz": "4820db058569be7350a81396fdedf9a28233b8061c9bcf607cf2d1058cbf437a",
+    "dist/2022-11-01/rust-std-beta-mips-unknown-linux-musl.tar.gz": "dfbc460e8322114bde5614b0b45e90066805adbaca999ccdc4f2aae456fc3f1f",
+    "dist/2022-11-01/rust-std-beta-mips-unknown-linux-musl.tar.xz": "d98c19268b0c84f44f1224f432847a93eb809a85ca48fbe2e4b68fb436bc36aa",
+    "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.gz": "8617edc6d62591d50dbadc4a7bc41b31b66bee6fee830af46636c5206027217f",
+    "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-gnuabi64.tar.xz": "98a6132c8dd7558eb5f44007fa681a3a91b2dfd98d1f68e59f0a4660dc37b500",
+    "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-muslabi64.tar.gz": "81f794c54d7a8c680c52a8fc1a0e479526744205d51266007fc3c542496957ba",
+    "dist/2022-11-01/rust-std-beta-mips64-unknown-linux-muslabi64.tar.xz": "dedc5b1a76f8454d1b3d7fda0a05398e5a9ae4cf16ddc4b44477799217a1fb75",
+    "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "6d9b3d469ae92e38144d9578de8cf0c891e4bf3e667e4e465eb6f0d498140c3c",
+    "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "f9deb84c24bd0f21ed02d763d3ad8dd92c009de4ceb2b78ec06d90d66609c5f6",
+    "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.gz": "073882815493668dd484b8f107efc047f6e07d8c563703d0e7f73ef33dae0efc",
+    "dist/2022-11-01/rust-std-beta-mips64el-unknown-linux-muslabi64.tar.xz": "d1ab3758d1b08937a3f98737ff9fad20377e5bc43d7ab3a9359b4131ea11dcbc",
+    "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-gnu.tar.gz": "bc82f3d23dfb7b331558180f474c334ca3798322e19cc64657cbe894d0682901",
+    "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-gnu.tar.xz": "fcc12f82ea0c02e8434420165f1ee072bf4587a82ff5ecf34d19f754ffc091ef",
+    "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-musl.tar.gz": "1c4507c7824c02b1af2857c88ff1624e9ead3f38c1456aa031586b43223e9490",
+    "dist/2022-11-01/rust-std-beta-mipsel-unknown-linux-musl.tar.xz": "932598fbcc35ee4958be4778450f5b809ce9eabb2aa3d7573fd79744ed4d18ad",
+    "dist/2022-11-01/rust-std-beta-nvptx64-nvidia-cuda.tar.gz": "c17f11707c1edef2353fba7e3f4800cecb8a273233825817b6d07ed78d6acd50",
+    "dist/2022-11-01/rust-std-beta-nvptx64-nvidia-cuda.tar.xz": "7e90a819b8887f0b1a3ab02fb9a56a9b9eb752408a7bb934c99c7f6ddda48a71",
+    "dist/2022-11-01/rust-std-beta-powerpc-unknown-linux-gnu.tar.gz": "c437a6fc7cd7869df7cdbb52c51ae1e602ed1206517c38689deb73da6d7b4877",
+    "dist/2022-11-01/rust-std-beta-powerpc-unknown-linux-gnu.tar.xz": "76c1fc55b16a809ab1c8dfce894899f40d24b20dc670d318a7679953beb6c3a1",
+    "dist/2022-11-01/rust-std-beta-powerpc64-unknown-linux-gnu.tar.gz": "d62396390fb85d5543a80ffbeaf7c32b5297a513dce14790124c35835813032b",
+    "dist/2022-11-01/rust-std-beta-powerpc64-unknown-linux-gnu.tar.xz": "4595485492c650aa53bb9deaeb425ea956f2052c5b5503bb477778f7bcaf6ac6",
+    "dist/2022-11-01/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.gz": "ee841bb8fbb0075a0bf51db2007bee2962830a89649c00fd15c67b31fd9226a3",
+    "dist/2022-11-01/rust-std-beta-powerpc64le-unknown-linux-gnu.tar.xz": "cac036fafa93f2860a5a2622394e12938c35e629ff81d7cc5930d99c980f9321",
+    "dist/2022-11-01/rust-std-beta-riscv32i-unknown-none-elf.tar.gz": "025d70e57d608b81d61799c84ccce9bca3603736c4d3e006fc662c3a7b39e8db",
+    "dist/2022-11-01/rust-std-beta-riscv32i-unknown-none-elf.tar.xz": "9450b1b1f95e188bcb9050085d612c8bef36e819881255fc20d70da1f45fa61e",
+    "dist/2022-11-01/rust-std-beta-riscv32imac-unknown-none-elf.tar.gz": "ff707c6d209f9d8e421fc530a11d41a46daaebdb4aebd5cfbaab761b2cf192ff",
+    "dist/2022-11-01/rust-std-beta-riscv32imac-unknown-none-elf.tar.xz": "8eb48a94c58440e2afc8ef7bbdbc725f403fe38724c0afde4e7c29a1ba2c7591",
+    "dist/2022-11-01/rust-std-beta-riscv32imc-unknown-none-elf.tar.gz": "e921a841b7a9e02e28182e91c921746042330d90f0478fc7e01230cb1b881c1c",
+    "dist/2022-11-01/rust-std-beta-riscv32imc-unknown-none-elf.tar.xz": "122135e161a4cc7dd857e3cb35b64ff7db450dcc07cbb990c8aa83e06bb4b346",
+    "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.gz": "084824d6daeca6a0662ef1e11df84c651138d8d4e7d5c8ef66c5811354b16211",
+    "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-linux-gnu.tar.xz": "09e3df606e10a0a59a67bf7b49825a04c23062e6050cebed674e0bdb2c396fcc",
+    "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-none-elf.tar.gz": "f9929f62ffec9c6b3342da8dd21b1c14526e033174a4f86015182acdbb93a985",
+    "dist/2022-11-01/rust-std-beta-riscv64gc-unknown-none-elf.tar.xz": "932450fc6b5e8fa4813886baa389b53c6ff1c5b1e71f7370017b9658b04fd13c",
+    "dist/2022-11-01/rust-std-beta-riscv64imac-unknown-none-elf.tar.gz": "e7776d188a04779e7f6a7257bf367d8671e7d5d804d74db426592f683cabf562",
+    "dist/2022-11-01/rust-std-beta-riscv64imac-unknown-none-elf.tar.xz": "bbc765adc116c6a1bcbf659853b7169d95b240ffc15227cbb1d60b46d63e120a",
+    "dist/2022-11-01/rust-std-beta-s390x-unknown-linux-gnu.tar.gz": "a0ff6e9ea827d7f93563aaec213eacd00efe4be9c921b448405b2af8bbf0066e",
+    "dist/2022-11-01/rust-std-beta-s390x-unknown-linux-gnu.tar.xz": "7603744cbbbbdec5b2a322aabe68751e848ac7379c710866c59dcc22e4b873bd",
+    "dist/2022-11-01/rust-std-beta-sparc64-unknown-linux-gnu.tar.gz": "1f67446eb09505e87a5218b8504dfc678d0a712a5add763362f3c74306010bea",
+    "dist/2022-11-01/rust-std-beta-sparc64-unknown-linux-gnu.tar.xz": "1baca6f0e7f18a8eb9efcf35bca4320a493d51f08e62bf96a31036e2f2c433fc",
+    "dist/2022-11-01/rust-std-beta-sparcv9-sun-solaris.tar.gz": "455e52fa3f232c2239112030483c0a657e7f69754d8d36ab72423c76c056fb68",
+    "dist/2022-11-01/rust-std-beta-sparcv9-sun-solaris.tar.xz": "913801ca45eb1d70c9ddfcdd66aa21edaafccc85acf9864e88991bf8a5a7cf25",
+    "dist/2022-11-01/rust-std-beta-thumbv6m-none-eabi.tar.gz": "14e4f69fbf710f16275ccb582a90eee1399ea1226945c7c96f75335df9118966",
+    "dist/2022-11-01/rust-std-beta-thumbv6m-none-eabi.tar.xz": "40549d9d9c923a73381b8e45628cfa1896d0e78caabf2aa921c767e0bc979136",
+    "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabi.tar.gz": "d40bd56883abc142155188674580c4e29100fd7303fccc70b0c55b964721a156",
+    "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabi.tar.xz": "874c97a01d06e1516a89797d7a6effeabf34afb4933956aa34e907a65ea78690",
+    "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabihf.tar.gz": "cf7acd2b4a083522c01f1909891aaba27502ea0a3a5eff93dfb41971f832bba6",
+    "dist/2022-11-01/rust-std-beta-thumbv7em-none-eabihf.tar.xz": "e9544acbefa3effe55537de85311b00077a0567d64345aa80414752037212b5f",
+    "dist/2022-11-01/rust-std-beta-thumbv7m-none-eabi.tar.gz": "247e9dae16f46c64da895528f3e902030110e2aad8270f169c636ca14bfc28aa",
+    "dist/2022-11-01/rust-std-beta-thumbv7m-none-eabi.tar.xz": "b7de9e8bf7b7d04fc9575390d69eacbcc62a39c35c81f37d2170424cffe6a356",
+    "dist/2022-11-01/rust-std-beta-thumbv7neon-linux-androideabi.tar.gz": "bd3dc986a11967e8ed050a88d03d1c0814b08cc1ab0cf929561fbf5a941a335e",
+    "dist/2022-11-01/rust-std-beta-thumbv7neon-linux-androideabi.tar.xz": "262b4c4ccbe20c9e913a7417c8ca72c6fb7e71f187103929057dcd0fc0b49cea",
+    "dist/2022-11-01/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.gz": "85f6a725e5a726afab9ae019944567b42ee769db98a8d3c335d449eca92344e0",
+    "dist/2022-11-01/rust-std-beta-thumbv7neon-unknown-linux-gnueabihf.tar.xz": "07e897f4320f249b3f458e44e5440591962105a3b6032b54f4448c0bd21da964",
+    "dist/2022-11-01/rust-std-beta-thumbv8m.base-none-eabi.tar.gz": "e92855841ae93990f88f3893a1bf511853fc3f10938eda767d5c7ff7d310aa4f",
+    "dist/2022-11-01/rust-std-beta-thumbv8m.base-none-eabi.tar.xz": "3c3412a67f769ead9e8bafbcb5ff6dfc8ef89f0d8234baee7b39ab9df9fadebf",
+    "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabi.tar.gz": "f3cd623fdd466e5c0b5749dc4e90a75122f1989f6fcae0ace8c76f3b394a0752",
+    "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabi.tar.xz": "3793ab2a42f1bc59ad560ad1af75ed90c49e25f665330b5b8ce50ed73ef88508",
+    "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabihf.tar.gz": "cc6c715e320c7fc5fd90f446f7c2ce6b356e95934d05f79c4e2d0fc304f212bd",
+    "dist/2022-11-01/rust-std-beta-thumbv8m.main-none-eabihf.tar.xz": "42a47ce6768b24c2b40c6a724003a401bfb37201a773e3c31ee413cc559cda70",
+    "dist/2022-11-01/rust-std-beta-wasm32-unknown-emscripten.tar.gz": "4c09e5b03a921d8c1d8a10d9535e81be3b3bbed961d229311cc691396ae10cbb",
+    "dist/2022-11-01/rust-std-beta-wasm32-unknown-emscripten.tar.xz": "775f7223bc5d962b5356a4884565a948d3cb5289fafe3e2eb2b8ad67550d72b4",
+    "dist/2022-11-01/rust-std-beta-wasm32-unknown-unknown.tar.gz": "bc027d9170132c36faa47da1ff8f26d26d383a5145cb9dd2dce20e769ea300ba",
+    "dist/2022-11-01/rust-std-beta-wasm32-unknown-unknown.tar.xz": "9a721d3550132930820d9b809074535d2b63ecb91d5c061effded92b503bf0c2",
+    "dist/2022-11-01/rust-std-beta-wasm32-wasi.tar.gz": "047d58ef5e10ab51a81dbc11646fca184945a1c52e7a91552449c67952c8d898",
+    "dist/2022-11-01/rust-std-beta-wasm32-wasi.tar.xz": "a490ce6ebc77a4a49c2fdeec471dd9e586b2aa26f1e7f2fc1323cc06b2b336d5",
+    "dist/2022-11-01/rust-std-beta-x86_64-apple-darwin.tar.gz": "df73bc81d446792d9366772944a04f69ad32f427e1949e05d4f7c202c350c269",
+    "dist/2022-11-01/rust-std-beta-x86_64-apple-darwin.tar.xz": "450aec3ec53594869bbf16ffe1713dfa19b8dcadd812a4af811bd56f1f58c929",
+    "dist/2022-11-01/rust-std-beta-x86_64-apple-ios.tar.gz": "fb698f63336a186983b09c2c49109dd080c22653f3367dabfcbae564144aff35",
+    "dist/2022-11-01/rust-std-beta-x86_64-apple-ios.tar.xz": "0d475ba4a4444f4da5fb39d26c9cdbc0352ea799d7e30f57e2e79d8c3c7a7021",
+    "dist/2022-11-01/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.gz": "137234fc37b93ef4fa543f4e33217079137b4dbb51efbea669b93e561932b5e9",
+    "dist/2022-11-01/rust-std-beta-x86_64-fortanix-unknown-sgx.tar.xz": "01e1978d9359a5112aa77409ff17c3d0e0dec774815f679065db6c6293aaa623",
+    "dist/2022-11-01/rust-std-beta-x86_64-fuchsia.tar.gz": "662e62862b1586f29372339319680c88b7cebe41e98401b5dd62e320755f0d62",
+    "dist/2022-11-01/rust-std-beta-x86_64-fuchsia.tar.xz": "4a644c6c85c8e427d68a669b0f598669023e2c0db2b69b94a7124c18772052dd",
+    "dist/2022-11-01/rust-std-beta-x86_64-linux-android.tar.gz": "752a57eb3de0060c1ffc6eb0af71d88d5f881b543b11b209593be2b18af1f902",
+    "dist/2022-11-01/rust-std-beta-x86_64-linux-android.tar.xz": "19effccfd9d63e955cb0736968c4c300c6d919217a64cde464c30a499ae9fd9c",
+    "dist/2022-11-01/rust-std-beta-x86_64-pc-solaris.tar.gz": "aa8a36ec1892c68a1c1ea0d9ac1b92b03c975a0d8ee538aaee5d757ad84d5b2e",
+    "dist/2022-11-01/rust-std-beta-x86_64-pc-solaris.tar.xz": "955ad79007d397a9e24d819e95017880b25424bdac01386cb8fc6d50247b1274",
+    "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-gnu.tar.gz": "9f15bf80a2384f2fd333dee41289fdd8529170192dcbdd8cba0a73d32715ccc3",
+    "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-gnu.tar.xz": "539bcefcd6b888c5f38abca47792dcff1676ef31eeb9a4a045703582262758c1",
+    "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-msvc.tar.gz": "748fd22a993be659f85c3799871c4de09a99fcd7805c6d0e9d5a18dddfd2e26b",
+    "dist/2022-11-01/rust-std-beta-x86_64-pc-windows-msvc.tar.xz": "68c22dfa2ef5ecd2d43661716e8a8394eaa36e8e960d34dc421bbbe57c3e0d23",
+    "dist/2022-11-01/rust-std-beta-x86_64-sun-solaris.tar.gz": "f06118445fc6671d491c61dd8e6ff83ca21fc1d692058eea072cbe01ff798fb2",
+    "dist/2022-11-01/rust-std-beta-x86_64-sun-solaris.tar.xz": "b3fdd56baadf3a8bffd17730d61b2ccef25ffa25d5cd826bb9a45940bf573fb5",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-freebsd.tar.gz": "2dfab0336a523182d200c7a6096fb29c199339b282ba03b469a9a1e5c5a5bb0b",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-freebsd.tar.xz": "ee5b9158ca0c2e829bb79ac526d17a2ba27ca4e305e134241ba1f8347a9bace5",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-illumos.tar.gz": "fe62b766d11e9ac55db5011a63086af5d87ce560c0656dc214c668db752675e4",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-illumos.tar.xz": "e4b1068de2812c62e7ac0ec080f605fa90123a94563dc4f898221275fbd5178b",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnu.tar.gz": "c5ce6885405ba4d1694a7eb767837320ece5a02133e94c1c22ac07143d6f752c",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnu.tar.xz": "eac46cc9200125be2548d6b9f2c2d37b046b8b43b25dd7f7347d88ef6795a3c7",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnux32.tar.gz": "1b3d1d051cf355eb26bf9de5096d984f83dc92fdeab3bdcd18d88152c0e2a2bf",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-gnux32.tar.xz": "a17cf4a9df1b1be17f5163f05665bc40638e62210d8e0623fb1afeeb96acad2a",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-musl.tar.gz": "90a2e5712bc37f28a0d1f71c54cc04233049c638e4f0592b50adea352e21038f",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-linux-musl.tar.xz": "ad76d090357f5e272b1598c35dd24137fb9950e1bdc50b9332fa1d2fcc33a00b",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-netbsd.tar.gz": "37e0954add559b24c08ad284fb80294e435491159db63ea78a6183af5926dcec",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-netbsd.tar.xz": "d6542bd592edd3924999e690416b6bc559486388add76fa77044114b70700fac",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-none.tar.gz": "d021e49b68b8321354d99ae0fe80a6b042ec798ca7fe37cc92d4f0c0480f7ebf",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-none.tar.xz": "f6202c50c6d3575fdb398a8c98adeb0d86794b60c3951887c90a9e4acb6a89c0",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-redox.tar.gz": "b8ca678975c0c18d0fda1bb118b35366d1261e366639b8bb455b6bc59388082f",
+    "dist/2022-11-01/rust-std-beta-x86_64-unknown-redox.tar.xz": "119f9e65dc3484f677064e068da42a1e7b8dc0be21d0cbf5185c9836589b39be",
+    "dist/2022-11-01/rustc-beta-aarch64-apple-darwin.tar.gz": "11aa79c56a9dea2d5305ed049485a1257912fc0dfca1feff37b768971f4c1701",
+    "dist/2022-11-01/rustc-beta-aarch64-apple-darwin.tar.xz": "a031051ccf97100bd8b4d2e4df7a67371cdf300df4697e1d05a7cec33a7d8c09",
+    "dist/2022-11-01/rustc-beta-aarch64-pc-windows-msvc.tar.gz": "4d015042d7d06929488f607bc56d925002e6f352d74fe192dc30e7feebb9947c",
+    "dist/2022-11-01/rustc-beta-aarch64-pc-windows-msvc.tar.xz": "d72824112c96514d927df46f6e755898d26ddd5b805f6c2c0411c773105ad61f",
+    "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-gnu.tar.gz": "3e70261ed7c130cb7256717cec0c37476961932be228e46e028818f9076dfccf",
+    "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-gnu.tar.xz": "452f07f63888cf27ca2d061751602bb07a43348eca9cab30db27940a36f496e5",
+    "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-musl.tar.gz": "995a6410305d43234eb94710ddc251bafd9f5fe4ecacc51c4dc1447f364be30a",
+    "dist/2022-11-01/rustc-beta-aarch64-unknown-linux-musl.tar.xz": "2d586e5d1a72194ce2798d4f07c873d52ea441cabe5040ff682664d618b98d4e",
+    "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabi.tar.gz": "65954bc862cd149cae2702f25b186fa2166d80cb45bfe6867d075381f2614464",
+    "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabi.tar.xz": "f3a5f8318efee7eb9ba4d861876b0a5415f308c9dc2cea751a10b2e259303627",
+    "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabihf.tar.gz": "d4be89140f0bd4ef9f73a1b54f949973ce560c4dd62c664974f82278ca0d6079",
+    "dist/2022-11-01/rustc-beta-arm-unknown-linux-gnueabihf.tar.xz": "5b381b513c27f95f9d170e9c532839a27facfe6eb4dd215c078b44fde40e3ba3",
+    "dist/2022-11-01/rustc-beta-armv7-unknown-linux-gnueabihf.tar.gz": "ffdf714a07408901962c861103b062adf334e0febc1abfa8c538c40b0070793e",
+    "dist/2022-11-01/rustc-beta-armv7-unknown-linux-gnueabihf.tar.xz": "ada55533236ef8c629ca72f929bb87db4b68f8c3d4c6fb3e7001f892a84a2b82",
+    "dist/2022-11-01/rustc-beta-i686-pc-windows-gnu.tar.gz": "b7e059973b61a4d7a0c96b4642629bf72668380a5ad8a2962181b1229ac2174c",
+    "dist/2022-11-01/rustc-beta-i686-pc-windows-gnu.tar.xz": "9aa3bc05e1782b8ff5d278f5b5baac4b0ae523ad8bba2bacd46e1bca11cd38b9",
+    "dist/2022-11-01/rustc-beta-i686-pc-windows-msvc.tar.gz": "acab77f5641be0d7102e6b911f134aa36b6fcad5ac594100889ed0e494eccca3",
+    "dist/2022-11-01/rustc-beta-i686-pc-windows-msvc.tar.xz": "e9af106c009e5fa0da36450a7a89a148ec176bd672ff636010846ab978804e4a",
+    "dist/2022-11-01/rustc-beta-i686-unknown-linux-gnu.tar.gz": "546e7b52f7f9e8c9a99163265dbc8a5ce65dac0fef4f6e1dc8b1bed79f0a24c3",
+    "dist/2022-11-01/rustc-beta-i686-unknown-linux-gnu.tar.xz": "b5ea7fc6016a4abcae3337261724ca2bd21025856134e1c2a1a1922d12ec19a8",
+    "dist/2022-11-01/rustc-beta-mips-unknown-linux-gnu.tar.gz": "0f3e0c8e7883dc7ebbec38e1f3446a33651ebba9a725443856b09ae7e8bcfec0",
+    "dist/2022-11-01/rustc-beta-mips-unknown-linux-gnu.tar.xz": "42871f7f098008f61f6cfd3cf78240156280cc7f5e52860d8125e22b3733a207",
+    "dist/2022-11-01/rustc-beta-mips64-unknown-linux-gnuabi64.tar.gz": "ded0d4da36a0658d46c6705c04fa40d0894b6e113776d2ef8e954e9675e98f9a",
+    "dist/2022-11-01/rustc-beta-mips64-unknown-linux-gnuabi64.tar.xz": "2f9ec1ba69a7abbe4efbc5fa00715f520b4c69792b96e98ed8a72e3f798eb137",
+    "dist/2022-11-01/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.gz": "090431409021fa0167576c717cf5daac750f9baf7badc3bc031547dad8dedb18",
+    "dist/2022-11-01/rustc-beta-mips64el-unknown-linux-gnuabi64.tar.xz": "0542d0336c8cdacf8a830d2a7c3218b76a00ae37db23fb2f12b928bb7b7dd488",
+    "dist/2022-11-01/rustc-beta-mipsel-unknown-linux-gnu.tar.gz": "2d6db76bc5242af8c2199c5e74f152bbd8103477855379e7c5c200b498ccf901",
+    "dist/2022-11-01/rustc-beta-mipsel-unknown-linux-gnu.tar.xz": "0dc803a305497cc905f3937691e4f1679c72a385b57ee931b19ac5347052c502",
+    "dist/2022-11-01/rustc-beta-powerpc-unknown-linux-gnu.tar.gz": "809f547fb5c27c7d15816642839f9ff5fee20f03a3ce390d5b2bfdc983a7c7e2",
+    "dist/2022-11-01/rustc-beta-powerpc-unknown-linux-gnu.tar.xz": "bd8403226676b78b40c7a494b3a89f9bed956e7eedf3a65a61cba41a6382f5b6",
+    "dist/2022-11-01/rustc-beta-powerpc64-unknown-linux-gnu.tar.gz": "12cd357dc72d67911a521dc0ea44a8d05bc4c214a7f6b9e88872ddc03811dc15",
+    "dist/2022-11-01/rustc-beta-powerpc64-unknown-linux-gnu.tar.xz": "d92f790cabb85373455b5adee9e692dc934dff60eccb70c077f29cde35e7cd00",
+    "dist/2022-11-01/rustc-beta-powerpc64le-unknown-linux-gnu.tar.gz": "a175a2b7d948459c12f44592c1ee5c79825a120557ff0c488fb0bd4e45c7ee99",
+    "dist/2022-11-01/rustc-beta-powerpc64le-unknown-linux-gnu.tar.xz": "d047a4ed562cc91469785fed44d97061d60e1f9c677b5de05245648373df111f",
+    "dist/2022-11-01/rustc-beta-riscv64gc-unknown-linux-gnu.tar.gz": "1b1f20032337e6a0b5e4745a3542a5638747bf2f3b62b2eb855c0ea1ac54d81c",
+    "dist/2022-11-01/rustc-beta-riscv64gc-unknown-linux-gnu.tar.xz": "c8a46c9c002ce19e940a449a4787055b4ad45076a606bd68626a1c8d892d8191",
+    "dist/2022-11-01/rustc-beta-s390x-unknown-linux-gnu.tar.gz": "547c670fd6a5f38f98e7b47daaf6822fd5a1abd5a7c11b6f2b5838cb145c615e",
+    "dist/2022-11-01/rustc-beta-s390x-unknown-linux-gnu.tar.xz": "16a0783135c22b64541cbf9201e5f84ab4befbc9ec0117f3e9639cd42dcb81bf",
+    "dist/2022-11-01/rustc-beta-x86_64-apple-darwin.tar.gz": "3121d060a0306c539334fb42c0c6edb6295eb4b5d05b63e55df98d5dc1cb0eba",
+    "dist/2022-11-01/rustc-beta-x86_64-apple-darwin.tar.xz": "4697febb60fdecb5cd70bde0cffad77cdcf8cce057349b4e1f26e3dd4f2f4a51",
+    "dist/2022-11-01/rustc-beta-x86_64-pc-windows-gnu.tar.gz": "c33bb5e98d83f0a7393c631b6b53eb4a8517bdbf506e1ceb6f0bdd8493fa24b9",
+    "dist/2022-11-01/rustc-beta-x86_64-pc-windows-gnu.tar.xz": "167e1ab52c4478e6aa8b2bea563f2d8caf3605158731a892181f9d24b027ffff",
+    "dist/2022-11-01/rustc-beta-x86_64-pc-windows-msvc.tar.gz": "efbe536d85810f2edb6bb7232617f12d3f208e077d177c24f507ff02c8e83a11",
+    "dist/2022-11-01/rustc-beta-x86_64-pc-windows-msvc.tar.xz": "b6acaa3206a3af7fe0e97d4d9211fc76ba972afcdd188443a72027dd34236658",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-freebsd.tar.gz": "8eb739094411afb56ad791b84aa2ddcd2c98b6ca5a4c1cd7fa631571702f1d67",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-freebsd.tar.xz": "4572c19bf416c188a3691cc9542422b92a124594bdf83c82213d07a3aaeef465",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-illumos.tar.gz": "eca080758173b3bee5a1ed7d04473a8334422fc58c762031877e690a255202c8",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-illumos.tar.xz": "68b1ced7efbd6bb4cac647e7417b2ad982f58a1cc546b9391213e85e5852ce6c",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-gnu.tar.gz": "3a4870b33c0f223dc295fcf3f1c4e331a2631dbc07279f4ca7452d86c5f6e730",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-gnu.tar.xz": "556821823576a5c0387f0dc89139d3cddc2a17072199607c352fe4b190b7f02f",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-musl.tar.gz": "4e1723c8f268eecc9bf3efb16241ce03bf109b9f7c6f4f62e430b7ccd1c092cb",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-linux-musl.tar.xz": "47bb3fb8f8529f19fa9725a43a57abd8bc3c7b2a30e17f86b137df0c57a3c549",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-netbsd.tar.gz": "530c24d950028d0745110672fad230da8a2a0e4cd4e5ac5afcf1ff8562288925",
+    "dist/2022-11-01/rustc-beta-x86_64-unknown-netbsd.tar.xz": "cd3654b33b3a8e7fbcde2e380bf2914cb07fe6f8355c8810a5bcfe3a05d63f84",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-apple-darwin.tar.gz": "47527c62b813c0612b80c864b3720b7e0673eb2dd762887254fd6a80f11c94b0",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-apple-darwin.tar.xz": "82248dd276ecc0fd45031ba131cb2c870a4b3c09b822d8ad4454f26f506d7810",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-pc-windows-msvc.tar.gz": "fdc9cc842850023e7c22ac22173a18aa5383a2e2fecb713c802e59d55cc5232d",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-pc-windows-msvc.tar.xz": "10ddbe6a89cadde47f6f52ef0c4f9ab08f4ced2281fadd1ecbc6a0e4736c9787",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.gz": "2135c6d129fa7ecd831e451e173c38677ea39975a91cd6092252e4c0bd93eeaa",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-gnu.tar.xz": "387d43021bd0ec1586155d1b977470646a68e2625fc192331b76180755687d37",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-musl.tar.gz": "41da916cbac667f5f238c3aee3bfb230c3345a4d625779c1fcf57813c9138696",
+    "dist/2022-11-01/rustfmt-nightly-aarch64-unknown-linux-musl.tar.xz": "f28cf712bc617f1755e78a7a442633a7aff78857b98d9aae473effc5684ce8aa",
+    "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.gz": "065fd7fdcb9f38a9c08b256b46627c8ce38a6433dc162034a306f4d4f4627a31",
+    "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabi.tar.xz": "1fc14261867b540e6d014cc5a21c557d0a4bb31d2619ae98a330585915365614",
+    "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.gz": "1a9ebea072c333e99a3339a87ac3971deb4fe2baca9bd0e8429321a81cce847f",
+    "dist/2022-11-01/rustfmt-nightly-arm-unknown-linux-gnueabihf.tar.xz": "1c7a72cf8e9cda52d02bd5f4244164aea829914087501cb0bedd75f05f464a91",
+    "dist/2022-11-01/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.gz": "1f8405178138601f65dbe10f93d326c705ea91f9e7200f253d6123f618d09ad8",
+    "dist/2022-11-01/rustfmt-nightly-armv7-unknown-linux-gnueabihf.tar.xz": "00365767eb739ecd82c6264795768baba07a101aacec59e137a7495afd0b3288",
+    "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-gnu.tar.gz": "b2e4c4672f440e1f97913497ee158280cb8ed70c81cb47a85e5382cb3de0b03c",
+    "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-gnu.tar.xz": "01bea91a3ab8203b32cbd1fb2945a1eca68179e8f4011e387a230587fc2736a4",
+    "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-msvc.tar.gz": "2d0db2a9f187d300c183cfe2ac6778547ab6492720c0e9df3e78f5b06004e758",
+    "dist/2022-11-01/rustfmt-nightly-i686-pc-windows-msvc.tar.xz": "b89b02b9fdedb9a93dee602dd9c818e97c397ef73c3f1d0164ddd2ab809cddc2",
+    "dist/2022-11-01/rustfmt-nightly-i686-unknown-linux-gnu.tar.gz": "4498a8e6d0ae7a793a9f3c84e3bbe9218c37053a1f3dd6a0b4ad7edd1a41493f",
+    "dist/2022-11-01/rustfmt-nightly-i686-unknown-linux-gnu.tar.xz": "27041aa61921b767be6670f0f08aad1a1ab8d09d0e86cd2e431e54744ed25d0b",
+    "dist/2022-11-01/rustfmt-nightly-mips-unknown-linux-gnu.tar.gz": "870923556049bd4be8da03fa6d876fa8249e4acf0ea2c83850c4e23a09fe577f",
+    "dist/2022-11-01/rustfmt-nightly-mips-unknown-linux-gnu.tar.xz": "8c05e1f60a59064c05db7522245d482b559ae858a5c9c772db81a05daa60a4c6",
+    "dist/2022-11-01/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.gz": "1a88c20701cc6f7dd2b3e32bef72a78936c39095a35237fc4a4b5a497790a048",
+    "dist/2022-11-01/rustfmt-nightly-mips64-unknown-linux-gnuabi64.tar.xz": "9b5ccf5413650144a79f382efd12204aeddf3421ea6f06615afc489cdf30691e",
+    "dist/2022-11-01/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.gz": "4bcf264ba7ce42aee79d76ba0f19818aff71ee666ac4ac417c2a60b0dafa8865",
+    "dist/2022-11-01/rustfmt-nightly-mips64el-unknown-linux-gnuabi64.tar.xz": "fd6ff248063cd53ee6b0538c8b3c8af1758ae5c42cc2f5fc805ab96799033f7d",
+    "dist/2022-11-01/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.gz": "a03cf4d831ba58d1e562d6fd48dd7558d9034046ae7050883eb1d0fc2cad6895",
+    "dist/2022-11-01/rustfmt-nightly-mipsel-unknown-linux-gnu.tar.xz": "30be7166fa091929d1a4b5eed4b72c4b5c94898861f4e91fb45a2b9ad4333ca6",
+    "dist/2022-11-01/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.gz": "59f3910a559994863f1910ffcf34cae348d0c07128d00ce5ac085bbca349f7f5",
+    "dist/2022-11-01/rustfmt-nightly-powerpc-unknown-linux-gnu.tar.xz": "817df1ddab344e47df34c73918c5bbb3a7b33048f8ac5c5794cb35624f5bce24",
+    "dist/2022-11-01/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.gz": "7825a5f19cb29245de96eb22183fbfc38b75eda0ba63d2255fa062f9c6764bbf",
+    "dist/2022-11-01/rustfmt-nightly-powerpc64-unknown-linux-gnu.tar.xz": "0d6384aa1162d821edb6d22326b0a1d481e6735d4343a70df7bead694bb71567",
+    "dist/2022-11-01/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.gz": "aef195c86920cfecafc29f80ce6a88c704f09d72011ad1fd462564bf858c75a6",
+    "dist/2022-11-01/rustfmt-nightly-powerpc64le-unknown-linux-gnu.tar.xz": "da43e621a113d88f7c4805f70cb5208bac66f97c68485a60f95cf11f5ae0f55c",
+    "dist/2022-11-01/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.gz": "2603b5061d059655e3298df94875fa4876d5ea9af1e04dd197ec5cefa3e1eb4c",
+    "dist/2022-11-01/rustfmt-nightly-riscv64gc-unknown-linux-gnu.tar.xz": "f79606c20ce3bf64a9ede63e878cda199e7f1b0b13f40bd51d7108b3d4c72cb0",
+    "dist/2022-11-01/rustfmt-nightly-s390x-unknown-linux-gnu.tar.gz": "f6c46ffbb38f8838c496e1eddea7d6f27392699abfafd0d13b234eee39238181",
+    "dist/2022-11-01/rustfmt-nightly-s390x-unknown-linux-gnu.tar.xz": "9244fc29cd3c32c971f44fcdaa26623b8976efaf0a4705b573798af5b0b0896e",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-apple-darwin.tar.gz": "bd502f9105d56af252da1038687a4e942a477c7047cac9730de7414cdbbfbc48",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-apple-darwin.tar.xz": "426785558da44683a554c542475c9402932c72d63148c37917e8cc6e429ad413",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-gnu.tar.gz": "52646c86ad464c5803f74ab37166dc1692383bc5fd94818bd579e518c327251e",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-gnu.tar.xz": "67d1490a41932c2a89981e18c9735d4437faedd1e708e26f75dfd21d4709488b",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-msvc.tar.gz": "9feb7b704a6d3e6b019a99ecd033042ce81a4b126e4288e0b4772266c6e0a65e",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-pc-windows-msvc.tar.xz": "fba240009d3f27e04200133120c46112ac64281e99952da44d6fe8a01557f236",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-freebsd.tar.gz": "ce15bda4992ada52f94dae6b1a0e220f26324acefb62094035abe112aa878fec",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-freebsd.tar.xz": "189a8579cf3fe99b9c084821ce1ee9bec6977470341e2ae45b859dcdacf65d21",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-illumos.tar.gz": "15abcd9e43f2c87fc894b3e280a99865508f9079badcbe7be07c6b79e85f01b4",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-illumos.tar.xz": "f52dab31a428e568518b00d3afc1426569810bcd20a7db1c0093200c6db86d24",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.gz": "57ec35b95a5fd803b2d4dacf7657847111a6cc9bda3cda962174965cd6005085",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-gnu.tar.xz": "96987349e20e3f602bb6f518924660c09a4575887730b1bbe36adee921921956",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-musl.tar.gz": "76d6f5882573169985f5b8a9e13cee8bbe3bd3b423ad287280a0809c6a5efc5a",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-linux-musl.tar.xz": "9a2c79685b4ac57efea65e43dafa28b59cead1c14e98f10e0196cb2cfd2fa0b6",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-netbsd.tar.gz": "0be98b45af7e666955e6e0adb5b4cc3f5517c8d144702b10daedd053450cd5d5",
+    "dist/2022-11-01/rustfmt-nightly-x86_64-unknown-netbsd.tar.xz": "ae9ac6e1c0e14bfba746f3a85bfa3f009113d0edbf880a2cf20ece6046ee27bb"
   }
 }
diff --git a/src/test/debuginfo/lexical-scope-in-if-let.rs b/src/test/debuginfo/lexical-scope-in-if-let.rs
index cdc37ce48fb..8fee459bd7a 100644
--- a/src/test/debuginfo/lexical-scope-in-if-let.rs
+++ b/src/test/debuginfo/lexical-scope-in-if-let.rs
@@ -58,19 +58,19 @@
 
 // cdb-command: g
 // cdb-command: dv
-// cdb-check:[...]y = true
-// cdb-check:[...]b = 0n456
 // cdb-check:[...]a = 0n123
 // cdb-check:[...]x = 0n42
+// cdb-check:[...]b = 0n456
+// cdb-check:[...]y = true
 
 // cdb-command: g
 // cdb-command: dv
 // cdb-check:[...]z = 0n10
 // cdb-check:[...]c = 0n789
-// cdb-check:[...]y = true
-// cdb-check:[...]b = 0n456
 // cdb-check:[...]a = 0n123
 // cdb-check:[...]x = 0n42
+// cdb-check:[...]b = 0n456
+// cdb-check:[...]y = true
 
 fn main() {
     let a = id(123);
diff --git a/src/test/incremental/issue-101518.rs b/src/test/incremental/issue-101518.rs
new file mode 100644
index 00000000000..501be175fce
--- /dev/null
+++ b/src/test/incremental/issue-101518.rs
@@ -0,0 +1,31 @@
+// revisions: cfail1
+// should-ice
+// error-pattern: forcing query
+// known-bug: #101518
+
+#[derive(PartialEq, Eq)]
+struct Id<'a> {
+    ns: &'a str,
+}
+fn visit_struct() {
+    let id = Id { ns: "random1" };
+    const FLAG: Id<'static> = Id {
+        ns: "needs_to_be_the_same",
+    };
+    match id {
+        FLAG => {}
+        _ => {}
+    }
+}
+fn visit_struct2() {
+    let id = Id { ns: "random2" };
+    const FLAG: Id<'static> = Id {
+        ns: "needs_to_be_the_same",
+    };
+    match id {
+        FLAG => {}
+        _ => {}
+    }
+}
+
+fn main() {}
diff --git a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
index a092f375291..e959e1b2f2c 100644
--- a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
+++ b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
@@ -30,14 +30,19 @@
 -                         debug s => _9;   // in scope 5 at $DIR/const_debuginfo.rs:+6:9: +6:10
 +                         debug s => const "hello, world!"; // in scope 5 at $DIR/const_debuginfo.rs:+6:9: +6:10
                           let _10: (bool, bool, u32); // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                          let _16: bool;   // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                          let _17: bool;   // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                          let _18: u32;    // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
                           scope 6 {
-                              debug f => _10; // in scope 6 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                              debug f => (bool, bool, u32){ .0 => _16, .1 => _17, .2 => _18, }; // in scope 6 at $DIR/const_debuginfo.rs:+8:9: +8:10
                               let _11: std::option::Option<u16>; // in scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
                               scope 7 {
                                   debug o => _11; // in scope 7 at $DIR/const_debuginfo.rs:+10:9: +10:10
                                   let _12: Point; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+                                  let _19: u32; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+                                  let _20: u32; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
                                   scope 8 {
-                                      debug p => _12; // in scope 8 at $DIR/const_debuginfo.rs:+12:9: +12:10
+                                      debug p => Point{ .0 => _19, .1 => _20, }; // in scope 8 at $DIR/const_debuginfo.rs:+12:9: +12:10
                                       let _13: u32; // in scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
                                       scope 9 {
 -                                         debug a => _13; // in scope 9 at $DIR/const_debuginfo.rs:+13:9: +13:10
@@ -78,19 +83,25 @@
                                            // mir::Constant
                                            // + span: $DIR/const_debuginfo.rs:14:13: 14:28
                                            // + literal: Const { ty: &str, val: Value(Slice(..)) }
-          StorageLive(_10);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
-          Deinit(_10);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
-          (_10.0: bool) = const true;      // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
-          (_10.1: bool) = const false;     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
-          (_10.2: u32) = const 123_u32;    // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          StorageLive(_16);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+          StorageLive(_17);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+          StorageLive(_18);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+          Deinit(_16);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          Deinit(_17);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          Deinit(_18);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          _16 = const true;                // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          _17 = const false;               // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          _18 = const 123_u32;             // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
           StorageLive(_11);                // scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
           Deinit(_11);                     // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
           ((_11 as Some).0: u16) = const 99_u16; // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
           discriminant(_11) = 1;           // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
-          StorageLive(_12);                // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
-          Deinit(_12);                     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
-          (_12.0: u32) = const 32_u32;     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
-          (_12.1: u32) = const 32_u32;     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          StorageLive(_19);                // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+          StorageLive(_20);                // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+          Deinit(_19);                     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          Deinit(_20);                     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          _19 = const 32_u32;              // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          _20 = const 32_u32;              // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
           StorageLive(_13);                // scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
           StorageLive(_14);                // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
           _14 = const 32_u32;              // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
@@ -101,9 +112,12 @@
           StorageDead(_14);                // scope 8 at $DIR/const_debuginfo.rs:+13:21: +13:22
           nop;                             // scope 0 at $DIR/const_debuginfo.rs:+0:11: +14:2
           StorageDead(_13);                // scope 8 at $DIR/const_debuginfo.rs:+14:1: +14:2
-          StorageDead(_12);                // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_19);                // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_20);                // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_11);                // scope 6 at $DIR/const_debuginfo.rs:+14:1: +14:2
-          StorageDead(_10);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_16);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_17);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_18);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_9);                 // scope 4 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_4);                 // scope 3 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_3);                 // scope 2 at $DIR/const_debuginfo.rs:+14:1: +14:2
diff --git a/src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir b/src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir
new file mode 100644
index 00000000000..cfc9a72e3b2
--- /dev/null
+++ b/src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir
@@ -0,0 +1,28 @@
+// MIR for `main` after PreCodegen
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/aggregate.rs:+0:11: +0:11
+    let _1: i32;                         // in scope 0 at $DIR/aggregate.rs:+1:9: +1:10
+    let mut _2: i32;                     // in scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+    let mut _3: (i32, i32, i32);         // in scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+    scope 1 {
+        debug x => _1;                   // in scope 1 at $DIR/aggregate.rs:+1:9: +1:10
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/aggregate.rs:+1:9: +1:10
+        StorageLive(_2);                 // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+        StorageLive(_3);                 // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        Deinit(_3);                      // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        (_3.0: i32) = const 0_i32;       // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        (_3.1: i32) = const 1_i32;       // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        (_3.2: i32) = const 2_i32;       // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        _2 = const 1_i32;                // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+        _1 = const 1_i32;                // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
+        StorageDead(_2);                 // scope 0 at $DIR/aggregate.rs:+1:27: +1:28
+        StorageDead(_3);                 // scope 0 at $DIR/aggregate.rs:+1:28: +1:29
+        _0 = const ();                   // scope 0 at $DIR/aggregate.rs:+0:11: +2:2
+        StorageDead(_1);                 // scope 0 at $DIR/aggregate.rs:+2:1: +2:2
+        return;                          // scope 0 at $DIR/aggregate.rs:+2:2: +2:2
+    }
+}
diff --git a/src/test/mir-opt/const_prop/aggregate.rs b/src/test/mir-opt/const_prop/aggregate.rs
index 493d0508a04..6a3080384da 100644
--- a/src/test/mir-opt/const_prop/aggregate.rs
+++ b/src/test/mir-opt/const_prop/aggregate.rs
@@ -2,6 +2,7 @@
 // compile-flags: -O
 
 // EMIT_MIR aggregate.main.ConstProp.diff
+// EMIT_MIR aggregate.main.PreCodegen.after.mir
 fn main() {
     let x = (0, 1, 2).1 + 0;
 }
diff --git a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff
index 186a9537356..2e4b0e79e9f 100644
--- a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff
@@ -8,8 +8,10 @@
       scope 1 {
           debug a => _1;                   // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+1:9: +1:10
           let mut _2: (i32, i32);          // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          let mut _6: i32;                 // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          let mut _7: i32;                 // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
           scope 2 {
-              debug x => _2;               // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+              debug x => (i32, i32){ .0 => _6, .1 => _7, }; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
               let _4: i32;                 // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
               scope 3 {
                   debug y => _4;           // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
@@ -30,23 +32,26 @@
       }
   
       bb1: {
-          StorageLive(_2);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
-          Deinit(_2);                      // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
-          (_2.0: i32) = const 1_i32;       // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
-          (_2.1: i32) = const 2_i32;       // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          StorageLive(_6);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          StorageLive(_7);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          Deinit(_6);                      // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          Deinit(_7);                      // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          _6 = const 1_i32;                // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          _7 = const 2_i32;                // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
           StorageLive(_3);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
           _3 = _1;                         // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
-          (_2.1: i32) = move _3;           // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:5: +3:12
+          _7 = move _3;                    // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:5: +3:12
           StorageDead(_3);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
           StorageLive(_4);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
-          _4 = (_2.1: i32);                // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:13: +4:16
+          _4 = _7;                         // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:13: +4:16
           StorageLive(_5);                 // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
--         _5 = (_2.0: i32);                // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
+-         _5 = _6;                         // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
 +         _5 = const 1_i32;                // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
           nop;                             // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+0:11: +6:2
           StorageDead(_5);                 // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
           StorageDead(_4);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
-          StorageDead(_2);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
+          StorageDead(_6);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
+          StorageDead(_7);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
           StorageDead(_1);                 // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
           return;                          // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+6:2: +6:2
       }
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
index 94aadfaf8d5..7e8ebd31ad1 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
@@ -10,6 +10,8 @@
       let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
       scope 1 {
           debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
           let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
@@ -51,13 +53,16 @@
           StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
-          StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
--         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+          StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
 +         _8 = const 42_u32;               // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
-          StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
           nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
           StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
           StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
index 94aadfaf8d5..7e8ebd31ad1 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
@@ -10,6 +10,8 @@
       let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
       scope 1 {
           debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
           let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
@@ -51,13 +53,16 @@
           StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
-          StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
--         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+          StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
 +         _8 = const 42_u32;               // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
-          StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
           nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
           StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
           StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir
new file mode 100644
index 00000000000..9db87cfc879
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir
@@ -0,0 +1,27 @@
+// MIR for `main` after PreCodegen
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+    let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+    scope 1 {
+        debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        let _2: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        scope 2 {
+            debug y => _2;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+            let _3: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            scope 3 {
+                debug z => _3;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        StorageLive(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        StorageLive(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+        StorageDead(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+    }
+}
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir
new file mode 100644
index 00000000000..9db87cfc879
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir
@@ -0,0 +1,27 @@
+// MIR for `main` after PreCodegen
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+    let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+    scope 1 {
+        debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        let _2: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        scope 2 {
+            debug y => _2;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+            let _3: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            scope 3 {
+                debug z => _3;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        StorageLive(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        StorageLive(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+        StorageDead(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+    }
+}
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff
new file mode 100644
index 00000000000..3f9f3b2eac7
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff
@@ -0,0 +1,72 @@
+- // MIR for `main` before ScalarReplacementOfAggregates
++ // MIR for `main` after ScalarReplacementOfAggregates
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+      let mut _2: (i32, bool);             // in scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      let mut _4: [i32; 6];                // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+      let _5: usize;                       // in scope 0 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+      let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          scope 2 {
+              debug y => _3;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+              let _8: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              scope 3 {
+                  debug z => _8;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      }
+  
+      bb1: {
+          _1 = move (_2.0: i32);           // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          StorageLive(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          StorageLive(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          StorageLive(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _5 = const 3_usize;              // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _6 = Len(_4);                    // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          _7 = Lt(_5, _6);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      }
+  
+      bb2: {
+          _3 = _4[_5];                     // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+-         StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+-         StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
++         StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
+          StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff
new file mode 100644
index 00000000000..3f9f3b2eac7
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff
@@ -0,0 +1,72 @@
+- // MIR for `main` before ScalarReplacementOfAggregates
++ // MIR for `main` after ScalarReplacementOfAggregates
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+      let mut _2: (i32, bool);             // in scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      let mut _4: [i32; 6];                // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+      let _5: usize;                       // in scope 0 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+      let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          scope 2 {
+              debug y => _3;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+              let _8: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              scope 3 {
+                  debug z => _8;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      }
+  
+      bb1: {
+          _1 = move (_2.0: i32);           // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          StorageLive(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          StorageLive(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          StorageLive(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _5 = const 3_usize;              // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _6 = Len(_4);                    // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          _7 = Lt(_5, _6);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      }
+  
+      bb2: {
+          _3 = _4[_5];                     // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+-         StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+-         StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
++         StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
+          StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.rs b/src/test/mir-opt/const_prop/optimizes_into_variable.rs
index c0fbd2558cd..02566654818 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.rs
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.rs
@@ -7,8 +7,10 @@ struct Point {
 }
 
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
+// EMIT_MIR optimizes_into_variable.main.ScalarReplacementOfAggregates.diff
 // EMIT_MIR optimizes_into_variable.main.ConstProp.diff
 // EMIT_MIR optimizes_into_variable.main.SimplifyLocals.after.mir
+// EMIT_MIR optimizes_into_variable.main.PreCodegen.after.mir
 fn main() {
     let x = 2 + 2;
     let y = [0, 1, 2, 3, 4, 5][3];
diff --git a/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot
index c00eae96e08..03df5c9504b 100644
--- a/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot
+++ b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot
@@ -2,5 +2,5 @@ digraph Cov_0_4 {
     graph [fontname="Courier, monospace"];
     node [fontname="Courier, monospace"];
     edge [fontname="Courier, monospace"];
-    bcb0__Cov_0_4 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 18:1-20:2<br/>    19:5-19:9: @0[0]: Coverage::Counter(1) for $DIR/coverage_graphviz.rs:18:1 - 20:2<br/>    20:2-20:2: @0.Return: return</td></tr><tr><td align="left" balign="left">bb0: Return</td></tr></table>>];
+    bcb0__Cov_0_4 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 18:1-20:2<br align="left"/>    19:5-19:9: @0[0]: Coverage::Counter(1) for $DIR/coverage_graphviz.rs:18:1 - 20:2<br align="left"/>    20:2-20:2: @0.Return: return</td></tr><tr><td align="left" balign="left">bb0: Return</td></tr></table>>];
 }
diff --git a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot
index ca0eb7e845a..fd21b14af25 100644
--- a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot
+++ b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot
@@ -2,10 +2,10 @@ digraph Cov_0_3 {
     graph [fontname="Courier, monospace"];
     node [fontname="Courier, monospace"];
     edge [fontname="Courier, monospace"];
-    bcb3__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb3</td></tr><tr><td align="left" balign="left">Counter(bcb3) at 13:10-13:10<br/>    13:10-13:10: @5[0]: Coverage::Counter(2) for $DIR/coverage_graphviz.rs:13:10 - 13:11</td></tr><tr><td align="left" balign="left">bb5: Goto</td></tr></table>>];
-    bcb2__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb2</td></tr><tr><td align="left" balign="left">Expression(bcb1:(bcb0 + bcb3) - bcb3) at 12:13-12:18<br/>    12:13-12:18: @4[0]: Coverage::Expression(4294967293) = 4294967294 + 0 for $DIR/coverage_graphviz.rs:15:1 - 15:2<br/>Expression(bcb2:(bcb1:(bcb0 + bcb3) - bcb3) + 0) at 15:2-15:2<br/>    15:2-15:2: @4.Return: return</td></tr><tr><td align="left" balign="left">bb4: Return</td></tr></table>>];
-    bcb1__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb1</td></tr><tr><td align="left" balign="left">Expression(bcb0 + bcb3) at 10:5-11:17<br/>    11:12-11:17: @2.Call: _2 = bar() -&gt; [return: bb3, unwind: bb6]</td></tr><tr><td align="left" balign="left">bb1: FalseUnwind<br/>bb2: Call</td></tr><tr><td align="left" balign="left">bb3: SwitchInt</td></tr></table>>];
-    bcb0__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 9:1-9:11<br/>    </td></tr><tr><td align="left" balign="left">bb0: Goto</td></tr></table>>];
+    bcb3__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb3</td></tr><tr><td align="left" balign="left">Counter(bcb3) at 13:10-13:10<br align="left"/>    13:10-13:10: @5[0]: Coverage::Counter(2) for $DIR/coverage_graphviz.rs:13:10 - 13:11</td></tr><tr><td align="left" balign="left">bb5: Goto</td></tr></table>>];
+    bcb2__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb2</td></tr><tr><td align="left" balign="left">Expression(bcb1:(bcb0 + bcb3) - bcb3) at 12:13-12:18<br align="left"/>    12:13-12:18: @4[0]: Coverage::Expression(4294967293) = 4294967294 + 0 for $DIR/coverage_graphviz.rs:15:1 - 15:2<br align="left"/>Expression(bcb2:(bcb1:(bcb0 + bcb3) - bcb3) + 0) at 15:2-15:2<br align="left"/>    15:2-15:2: @4.Return: return</td></tr><tr><td align="left" balign="left">bb4: Return</td></tr></table>>];
+    bcb1__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb1</td></tr><tr><td align="left" balign="left">Expression(bcb0 + bcb3) at 10:5-11:17<br align="left"/>    11:12-11:17: @2.Call: _2 = bar() -&gt; [return: bb3, unwind: bb6]</td></tr><tr><td align="left" balign="left">bb1: FalseUnwind<br align="left"/>bb2: Call</td></tr><tr><td align="left" balign="left">bb3: SwitchInt</td></tr></table>>];
+    bcb0__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 9:1-9:11<br align="left"/>    </td></tr><tr><td align="left" balign="left">bb0: Goto</td></tr></table>>];
     bcb3__Cov_0_3 -> bcb1__Cov_0_3 [label=<>];
     bcb1__Cov_0_3 -> bcb3__Cov_0_3 [label=<false>];
     bcb1__Cov_0_3 -> bcb2__Cov_0_3 [label=<otherwise>];
diff --git a/src/test/mir-opt/dataflow-const-prop/cast.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/cast.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..bf9ab866938
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/cast.main.DataflowConstProp.diff
@@ -0,0 +1,37 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/cast.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/cast.rs:+1:9: +1:10
+      let mut _3: u8;                      // in scope 0 at $DIR/cast.rs:+2:13: +2:20
+      let mut _4: i32;                     // in scope 0 at $DIR/cast.rs:+2:13: +2:14
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/cast.rs:+1:9: +1:10
+          let _2: u8;                      // in scope 1 at $DIR/cast.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _2;               // in scope 2 at $DIR/cast.rs:+2:9: +2:10
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/cast.rs:+1:9: +1:10
+          _1 = const 257_i32;              // scope 0 at $DIR/cast.rs:+1:13: +1:16
+          StorageLive(_2);                 // scope 1 at $DIR/cast.rs:+2:9: +2:10
+          StorageLive(_3);                 // scope 1 at $DIR/cast.rs:+2:13: +2:20
+          StorageLive(_4);                 // scope 1 at $DIR/cast.rs:+2:13: +2:14
+-         _4 = _1;                         // scope 1 at $DIR/cast.rs:+2:13: +2:14
+-         _3 = move _4 as u8 (IntToInt);   // scope 1 at $DIR/cast.rs:+2:13: +2:20
++         _4 = const 257_i32;              // scope 1 at $DIR/cast.rs:+2:13: +2:14
++         _3 = const 1_u8;                 // scope 1 at $DIR/cast.rs:+2:13: +2:20
+          StorageDead(_4);                 // scope 1 at $DIR/cast.rs:+2:19: +2:20
+-         _2 = Add(move _3, const 1_u8);   // scope 1 at $DIR/cast.rs:+2:13: +2:24
++         _2 = const 2_u8;                 // scope 1 at $DIR/cast.rs:+2:13: +2:24
+          StorageDead(_3);                 // scope 1 at $DIR/cast.rs:+2:23: +2:24
+          _0 = const ();                   // scope 0 at $DIR/cast.rs:+0:11: +3:2
+          StorageDead(_2);                 // scope 1 at $DIR/cast.rs:+3:1: +3:2
+          StorageDead(_1);                 // scope 0 at $DIR/cast.rs:+3:1: +3:2
+          return;                          // scope 0 at $DIR/cast.rs:+3:2: +3:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/cast.rs b/src/test/mir-opt/dataflow-const-prop/cast.rs
new file mode 100644
index 00000000000..484403f7f0e
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/cast.rs
@@ -0,0 +1,7 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR cast.main.DataflowConstProp.diff
+fn main() {
+    let a = 257;
+    let b = a as u8 + 1;
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..a4ebd0c8c18
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/checked.main.DataflowConstProp.diff
@@ -0,0 +1,80 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/checked.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/checked.rs:+1:9: +1:10
+      let mut _4: i32;                     // in scope 0 at $DIR/checked.rs:+3:13: +3:14
+      let mut _5: i32;                     // in scope 0 at $DIR/checked.rs:+3:17: +3:18
+      let mut _6: (i32, bool);             // in scope 0 at $DIR/checked.rs:+3:13: +3:18
+      let mut _9: i32;                     // in scope 0 at $DIR/checked.rs:+6:13: +6:14
+      let mut _10: (i32, bool);            // in scope 0 at $DIR/checked.rs:+6:13: +6:18
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/checked.rs:+1:9: +1:10
+          let _2: i32;                     // in scope 1 at $DIR/checked.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _2;               // in scope 2 at $DIR/checked.rs:+2:9: +2:10
+              let _3: i32;                 // in scope 2 at $DIR/checked.rs:+3:9: +3:10
+              scope 3 {
+                  debug c => _3;           // in scope 3 at $DIR/checked.rs:+3:9: +3:10
+                  let _7: i32;             // in scope 3 at $DIR/checked.rs:+5:9: +5:10
+                  scope 4 {
+                      debug d => _7;       // in scope 4 at $DIR/checked.rs:+5:9: +5:10
+                      let _8: i32;         // in scope 4 at $DIR/checked.rs:+6:9: +6:10
+                      scope 5 {
+                          debug e => _8;   // in scope 5 at $DIR/checked.rs:+6:9: +6:10
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/checked.rs:+1:9: +1:10
+          _1 = const 1_i32;                // scope 0 at $DIR/checked.rs:+1:13: +1:14
+          StorageLive(_2);                 // scope 1 at $DIR/checked.rs:+2:9: +2:10
+          _2 = const 2_i32;                // scope 1 at $DIR/checked.rs:+2:13: +2:14
+          StorageLive(_3);                 // scope 2 at $DIR/checked.rs:+3:9: +3:10
+          StorageLive(_4);                 // scope 2 at $DIR/checked.rs:+3:13: +3:14
+-         _4 = _1;                         // scope 2 at $DIR/checked.rs:+3:13: +3:14
++         _4 = const 1_i32;                // scope 2 at $DIR/checked.rs:+3:13: +3:14
+          StorageLive(_5);                 // scope 2 at $DIR/checked.rs:+3:17: +3:18
+-         _5 = _2;                         // scope 2 at $DIR/checked.rs:+3:17: +3:18
+-         _6 = CheckedAdd(_4, _5);         // scope 2 at $DIR/checked.rs:+3:13: +3:18
+-         assert(!move (_6.1: bool), "attempt to compute `{} + {}`, which would overflow", move _4, move _5) -> bb1; // scope 2 at $DIR/checked.rs:+3:13: +3:18
++         _5 = const 2_i32;                // scope 2 at $DIR/checked.rs:+3:17: +3:18
++         _6 = CheckedAdd(const 1_i32, const 2_i32); // scope 2 at $DIR/checked.rs:+3:13: +3:18
++         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 1_i32, const 2_i32) -> bb1; // scope 2 at $DIR/checked.rs:+3:13: +3:18
+      }
+  
+      bb1: {
+-         _3 = move (_6.0: i32);           // scope 2 at $DIR/checked.rs:+3:13: +3:18
++         _3 = const 3_i32;                // scope 2 at $DIR/checked.rs:+3:13: +3:18
+          StorageDead(_5);                 // scope 2 at $DIR/checked.rs:+3:17: +3:18
+          StorageDead(_4);                 // scope 2 at $DIR/checked.rs:+3:17: +3:18
+          StorageLive(_7);                 // scope 3 at $DIR/checked.rs:+5:9: +5:10
+          _7 = const _;                    // scope 3 at $DIR/checked.rs:+5:13: +5:21
+          StorageLive(_8);                 // scope 4 at $DIR/checked.rs:+6:9: +6:10
+          StorageLive(_9);                 // scope 4 at $DIR/checked.rs:+6:13: +6:14
+-         _9 = _7;                         // scope 4 at $DIR/checked.rs:+6:13: +6:14
+-         _10 = CheckedAdd(_9, const 1_i32); // scope 4 at $DIR/checked.rs:+6:13: +6:18
+-         assert(!move (_10.1: bool), "attempt to compute `{} + {}`, which would overflow", move _9, const 1_i32) -> bb2; // scope 4 at $DIR/checked.rs:+6:13: +6:18
++         _9 = const i32::MAX;             // scope 4 at $DIR/checked.rs:+6:13: +6:14
++         _10 = CheckedAdd(const i32::MAX, const 1_i32); // scope 4 at $DIR/checked.rs:+6:13: +6:18
++         assert(!move (_10.1: bool), "attempt to compute `{} + {}`, which would overflow", const i32::MAX, const 1_i32) -> bb2; // scope 4 at $DIR/checked.rs:+6:13: +6:18
+      }
+  
+      bb2: {
+-         _8 = move (_10.0: i32);          // scope 4 at $DIR/checked.rs:+6:13: +6:18
++         _8 = const i32::MIN;             // scope 4 at $DIR/checked.rs:+6:13: +6:18
+          StorageDead(_9);                 // scope 4 at $DIR/checked.rs:+6:17: +6:18
+          _0 = const ();                   // scope 0 at $DIR/checked.rs:+0:11: +7:2
+          StorageDead(_8);                 // scope 4 at $DIR/checked.rs:+7:1: +7:2
+          StorageDead(_7);                 // scope 3 at $DIR/checked.rs:+7:1: +7:2
+          StorageDead(_3);                 // scope 2 at $DIR/checked.rs:+7:1: +7:2
+          StorageDead(_2);                 // scope 1 at $DIR/checked.rs:+7:1: +7:2
+          StorageDead(_1);                 // scope 0 at $DIR/checked.rs:+7:1: +7:2
+          return;                          // scope 0 at $DIR/checked.rs:+7:2: +7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/checked.rs b/src/test/mir-opt/dataflow-const-prop/checked.rs
new file mode 100644
index 00000000000..0738a4ee53b
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/checked.rs
@@ -0,0 +1,13 @@
+// unit-test: DataflowConstProp
+// compile-flags: -Coverflow-checks=on
+
+// EMIT_MIR checked.main.DataflowConstProp.diff
+#[allow(arithmetic_overflow)]
+fn main() {
+    let a = 1;
+    let b = 2;
+    let c = a + b;
+
+    let d = i32::MAX;
+    let e = d + 1;
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/enum.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/enum.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..2ced794e628
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/enum.main.DataflowConstProp.diff
@@ -0,0 +1,61 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/enum.rs:+0:11: +0:11
+      let _1: E;                           // in scope 0 at $DIR/enum.rs:+1:9: +1:10
+      let mut _3: isize;                   // in scope 0 at $DIR/enum.rs:+2:23: +2:31
+      scope 1 {
+          debug e => _1;                   // in scope 1 at $DIR/enum.rs:+1:9: +1:10
+          let _2: i32;                     // in scope 1 at $DIR/enum.rs:+2:9: +2:10
+          let _4: i32;                     // in scope 1 at $DIR/enum.rs:+2:29: +2:30
+          let _5: i32;                     // in scope 1 at $DIR/enum.rs:+2:44: +2:45
+          scope 2 {
+              debug x => _2;               // in scope 2 at $DIR/enum.rs:+2:9: +2:10
+          }
+          scope 3 {
+              debug x => _4;               // in scope 3 at $DIR/enum.rs:+2:29: +2:30
+          }
+          scope 4 {
+              debug x => _5;               // in scope 4 at $DIR/enum.rs:+2:44: +2:45
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/enum.rs:+1:9: +1:10
+          Deinit(_1);                      // scope 0 at $DIR/enum.rs:+1:13: +1:21
+          ((_1 as V1).0: i32) = const 0_i32; // scope 0 at $DIR/enum.rs:+1:13: +1:21
+          discriminant(_1) = 0;            // scope 0 at $DIR/enum.rs:+1:13: +1:21
+          StorageLive(_2);                 // scope 1 at $DIR/enum.rs:+2:9: +2:10
+          _3 = discriminant(_1);           // scope 1 at $DIR/enum.rs:+2:19: +2:20
+          switchInt(move _3) -> [0_isize: bb3, 1_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/enum.rs:+2:13: +2:20
+      }
+  
+      bb1: {
+          StorageLive(_5);                 // scope 1 at $DIR/enum.rs:+2:44: +2:45
+          _5 = ((_1 as V2).0: i32);        // scope 1 at $DIR/enum.rs:+2:44: +2:45
+          _2 = _5;                         // scope 4 at $DIR/enum.rs:+2:50: +2:51
+          StorageDead(_5);                 // scope 1 at $DIR/enum.rs:+2:50: +2:51
+          goto -> bb4;                     // scope 1 at $DIR/enum.rs:+2:50: +2:51
+      }
+  
+      bb2: {
+          unreachable;                     // scope 1 at $DIR/enum.rs:+2:19: +2:20
+      }
+  
+      bb3: {
+          StorageLive(_4);                 // scope 1 at $DIR/enum.rs:+2:29: +2:30
+          _4 = ((_1 as V1).0: i32);        // scope 1 at $DIR/enum.rs:+2:29: +2:30
+          _2 = _4;                         // scope 3 at $DIR/enum.rs:+2:35: +2:36
+          StorageDead(_4);                 // scope 1 at $DIR/enum.rs:+2:35: +2:36
+          goto -> bb4;                     // scope 1 at $DIR/enum.rs:+2:35: +2:36
+      }
+  
+      bb4: {
+          _0 = const ();                   // scope 0 at $DIR/enum.rs:+0:11: +3:2
+          StorageDead(_2);                 // scope 1 at $DIR/enum.rs:+3:1: +3:2
+          StorageDead(_1);                 // scope 0 at $DIR/enum.rs:+3:1: +3:2
+          return;                          // scope 0 at $DIR/enum.rs:+3:2: +3:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/enum.rs b/src/test/mir-opt/dataflow-const-prop/enum.rs
new file mode 100644
index 00000000000..13288577dea
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/enum.rs
@@ -0,0 +1,13 @@
+// unit-test: DataflowConstProp
+
+// Not trackable, because variants could be aliased.
+enum E {
+    V1(i32),
+    V2(i32)
+}
+
+// EMIT_MIR enum.main.DataflowConstProp.diff
+fn main() {
+    let e = E::V1(0);
+    let x = match e { E::V1(x) => x, E::V2(x) => x };
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/if.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/if.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..26808c70fbf
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/if.main.DataflowConstProp.diff
@@ -0,0 +1,112 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/if.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/if.rs:+1:9: +1:10
+      let mut _3: bool;                    // in scope 0 at $DIR/if.rs:+2:16: +2:22
+      let mut _4: i32;                     // in scope 0 at $DIR/if.rs:+2:16: +2:17
+      let mut _6: i32;                     // in scope 0 at $DIR/if.rs:+3:13: +3:14
+      let mut _8: bool;                    // in scope 0 at $DIR/if.rs:+5:16: +5:22
+      let mut _9: i32;                     // in scope 0 at $DIR/if.rs:+5:16: +5:17
+      let mut _10: i32;                    // in scope 0 at $DIR/if.rs:+5:36: +5:37
+      let mut _12: i32;                    // in scope 0 at $DIR/if.rs:+6:13: +6:14
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/if.rs:+1:9: +1:10
+          let _2: i32;                     // in scope 1 at $DIR/if.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _2;               // in scope 2 at $DIR/if.rs:+2:9: +2:10
+              let _5: i32;                 // in scope 2 at $DIR/if.rs:+3:9: +3:10
+              scope 3 {
+                  debug c => _5;           // in scope 3 at $DIR/if.rs:+3:9: +3:10
+                  let _7: i32;             // in scope 3 at $DIR/if.rs:+5:9: +5:10
+                  scope 4 {
+                      debug d => _7;       // in scope 4 at $DIR/if.rs:+5:9: +5:10
+                      let _11: i32;        // in scope 4 at $DIR/if.rs:+6:9: +6:10
+                      scope 5 {
+                          debug e => _11;  // in scope 5 at $DIR/if.rs:+6:9: +6:10
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/if.rs:+1:9: +1:10
+          _1 = const 1_i32;                // scope 0 at $DIR/if.rs:+1:13: +1:14
+          StorageLive(_2);                 // scope 1 at $DIR/if.rs:+2:9: +2:10
+          StorageLive(_3);                 // scope 1 at $DIR/if.rs:+2:16: +2:22
+          StorageLive(_4);                 // scope 1 at $DIR/if.rs:+2:16: +2:17
+-         _4 = _1;                         // scope 1 at $DIR/if.rs:+2:16: +2:17
+-         _3 = Eq(move _4, const 1_i32);   // scope 1 at $DIR/if.rs:+2:16: +2:22
++         _4 = const 1_i32;                // scope 1 at $DIR/if.rs:+2:16: +2:17
++         _3 = const true;                 // scope 1 at $DIR/if.rs:+2:16: +2:22
+          StorageDead(_4);                 // scope 1 at $DIR/if.rs:+2:21: +2:22
+-         switchInt(move _3) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/if.rs:+2:16: +2:22
++         switchInt(const true) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/if.rs:+2:16: +2:22
+      }
+  
+      bb1: {
+          _2 = const 2_i32;                // scope 1 at $DIR/if.rs:+2:25: +2:26
+          goto -> bb3;                     // scope 1 at $DIR/if.rs:+2:13: +2:39
+      }
+  
+      bb2: {
+          _2 = const 3_i32;                // scope 1 at $DIR/if.rs:+2:36: +2:37
+          goto -> bb3;                     // scope 1 at $DIR/if.rs:+2:13: +2:39
+      }
+  
+      bb3: {
+          StorageDead(_3);                 // scope 1 at $DIR/if.rs:+2:38: +2:39
+          StorageLive(_5);                 // scope 2 at $DIR/if.rs:+3:9: +3:10
+          StorageLive(_6);                 // scope 2 at $DIR/if.rs:+3:13: +3:14
+-         _6 = _2;                         // scope 2 at $DIR/if.rs:+3:13: +3:14
+-         _5 = Add(move _6, const 1_i32);  // scope 2 at $DIR/if.rs:+3:13: +3:18
++         _6 = const 2_i32;                // scope 2 at $DIR/if.rs:+3:13: +3:14
++         _5 = const 3_i32;                // scope 2 at $DIR/if.rs:+3:13: +3:18
+          StorageDead(_6);                 // scope 2 at $DIR/if.rs:+3:17: +3:18
+          StorageLive(_7);                 // scope 3 at $DIR/if.rs:+5:9: +5:10
+          StorageLive(_8);                 // scope 3 at $DIR/if.rs:+5:16: +5:22
+          StorageLive(_9);                 // scope 3 at $DIR/if.rs:+5:16: +5:17
+-         _9 = _1;                         // scope 3 at $DIR/if.rs:+5:16: +5:17
+-         _8 = Eq(move _9, const 1_i32);   // scope 3 at $DIR/if.rs:+5:16: +5:22
++         _9 = const 1_i32;                // scope 3 at $DIR/if.rs:+5:16: +5:17
++         _8 = const true;                 // scope 3 at $DIR/if.rs:+5:16: +5:22
+          StorageDead(_9);                 // scope 3 at $DIR/if.rs:+5:21: +5:22
+-         switchInt(move _8) -> [false: bb5, otherwise: bb4]; // scope 3 at $DIR/if.rs:+5:16: +5:22
++         switchInt(const true) -> [false: bb5, otherwise: bb4]; // scope 3 at $DIR/if.rs:+5:16: +5:22
+      }
+  
+      bb4: {
+-         _7 = _1;                         // scope 3 at $DIR/if.rs:+5:25: +5:26
++         _7 = const 1_i32;                // scope 3 at $DIR/if.rs:+5:25: +5:26
+          goto -> bb6;                     // scope 3 at $DIR/if.rs:+5:13: +5:43
+      }
+  
+      bb5: {
+          StorageLive(_10);                // scope 3 at $DIR/if.rs:+5:36: +5:37
+          _10 = _1;                        // scope 3 at $DIR/if.rs:+5:36: +5:37
+          _7 = Add(move _10, const 1_i32); // scope 3 at $DIR/if.rs:+5:36: +5:41
+          StorageDead(_10);                // scope 3 at $DIR/if.rs:+5:40: +5:41
+          goto -> bb6;                     // scope 3 at $DIR/if.rs:+5:13: +5:43
+      }
+  
+      bb6: {
+          StorageDead(_8);                 // scope 3 at $DIR/if.rs:+5:42: +5:43
+          StorageLive(_11);                // scope 4 at $DIR/if.rs:+6:9: +6:10
+          StorageLive(_12);                // scope 4 at $DIR/if.rs:+6:13: +6:14
+-         _12 = _7;                        // scope 4 at $DIR/if.rs:+6:13: +6:14
+-         _11 = Add(move _12, const 1_i32); // scope 4 at $DIR/if.rs:+6:13: +6:18
++         _12 = const 1_i32;               // scope 4 at $DIR/if.rs:+6:13: +6:14
++         _11 = const 2_i32;               // scope 4 at $DIR/if.rs:+6:13: +6:18
+          StorageDead(_12);                // scope 4 at $DIR/if.rs:+6:17: +6:18
+          _0 = const ();                   // scope 0 at $DIR/if.rs:+0:11: +7:2
+          StorageDead(_11);                // scope 4 at $DIR/if.rs:+7:1: +7:2
+          StorageDead(_7);                 // scope 3 at $DIR/if.rs:+7:1: +7:2
+          StorageDead(_5);                 // scope 2 at $DIR/if.rs:+7:1: +7:2
+          StorageDead(_2);                 // scope 1 at $DIR/if.rs:+7:1: +7:2
+          StorageDead(_1);                 // scope 0 at $DIR/if.rs:+7:1: +7:2
+          return;                          // scope 0 at $DIR/if.rs:+7:2: +7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/if.rs b/src/test/mir-opt/dataflow-const-prop/if.rs
new file mode 100644
index 00000000000..34fc35790c1
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/if.rs
@@ -0,0 +1,11 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR if.main.DataflowConstProp.diff
+fn main() {
+    let a = 1;
+    let b = if a == 1 { 2 } else { 3 };
+    let c = b + 1;
+
+    let d = if a == 1 { a } else { a + 1 };
+    let e = d + 1;
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..bf4557ed3d9
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/inherit_overflow.main.DataflowConstProp.diff
@@ -0,0 +1,45 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/inherit_overflow.rs:+0:11: +0:11
+      let mut _1: u8;                      // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+      let mut _2: u8;                      // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+      let mut _3: u8;                      // in scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+      scope 1 {
+      }
+      scope 2 (inlined <u8 as Add>::add) { // at $DIR/inherit_overflow.rs:7:13: 7:47
+          debug self => _2;                // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          debug other => _3;               // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          let mut _4: u8;                  // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          let mut _5: u8;                  // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          let mut _6: (u8, bool);          // in scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+          StorageLive(_2);                 // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+          _2 = const u8::MAX;              // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+          StorageLive(_3);                 // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+          _3 = const 1_u8;                 // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+          StorageLive(_4);                 // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          _4 = const u8::MAX;              // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          StorageLive(_5);                 // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          _5 = const 1_u8;                 // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          _6 = CheckedAdd(const u8::MAX, const 1_u8); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          assert(!move (_6.1: bool), "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+      }
+  
+      bb1: {
+-         _1 = move (_6.0: u8);            // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
++         _1 = const 0_u8;                 // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          StorageDead(_5);                 // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          StorageDead(_4);                 // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
+          StorageDead(_3);                 // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+          StorageDead(_2);                 // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
+          StorageDead(_1);                 // scope 0 at $DIR/inherit_overflow.rs:+3:47: +3:48
+          nop;                             // scope 0 at $DIR/inherit_overflow.rs:+0:11: +4:2
+          return;                          // scope 0 at $DIR/inherit_overflow.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/inherit_overflow.rs b/src/test/mir-opt/dataflow-const-prop/inherit_overflow.rs
new file mode 100644
index 00000000000..2f2d9d0102d
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/inherit_overflow.rs
@@ -0,0 +1,8 @@
+// compile-flags: -Zunsound-mir-opts
+
+// EMIT_MIR inherit_overflow.main.DataflowConstProp.diff
+fn main() {
+    // After inlining, this will contain a `CheckedBinaryOp`. The overflow
+    // must be ignored by the constant propagation to avoid triggering a panic.
+    let _ = <u8 as std::ops::Add>::add(255, 1);
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/issue_81605.f.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/issue_81605.f.DataflowConstProp.diff
new file mode 100644
index 00000000000..881d80f7c03
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/issue_81605.f.DataflowConstProp.diff
@@ -0,0 +1,35 @@
+- // MIR for `f` before DataflowConstProp
++ // MIR for `f` after DataflowConstProp
+  
+  fn f() -> usize {
+      let mut _0: usize;                   // return place in scope 0 at $DIR/issue_81605.rs:+0:11: +0:16
+      let mut _1: usize;                   // in scope 0 at $DIR/issue_81605.rs:+1:9: +1:33
+      let mut _2: bool;                    // in scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/issue_81605.rs:+1:9: +1:33
+          StorageLive(_2);                 // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
+          _2 = const true;                 // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
+-         switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
++         switchInt(const true) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/issue_81605.rs:+1:12: +1:16
+      }
+  
+      bb1: {
+          _1 = const 1_usize;              // scope 0 at $DIR/issue_81605.rs:+1:19: +1:20
+          goto -> bb3;                     // scope 0 at $DIR/issue_81605.rs:+1:9: +1:33
+      }
+  
+      bb2: {
+          _1 = const 2_usize;              // scope 0 at $DIR/issue_81605.rs:+1:30: +1:31
+          goto -> bb3;                     // scope 0 at $DIR/issue_81605.rs:+1:9: +1:33
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/issue_81605.rs:+1:32: +1:33
+-         _0 = Add(const 1_usize, move _1); // scope 0 at $DIR/issue_81605.rs:+1:5: +1:33
++         _0 = const 2_usize;              // scope 0 at $DIR/issue_81605.rs:+1:5: +1:33
+          StorageDead(_1);                 // scope 0 at $DIR/issue_81605.rs:+1:32: +1:33
+          return;                          // scope 0 at $DIR/issue_81605.rs:+2:2: +2:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/issue_81605.rs b/src/test/mir-opt/dataflow-const-prop/issue_81605.rs
new file mode 100644
index 00000000000..d75e2a28bef
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/issue_81605.rs
@@ -0,0 +1,10 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR issue_81605.f.DataflowConstProp.diff
+fn f() -> usize {
+    1 + if true { 1 } else { 2 }
+}
+
+fn main() {
+    f();
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/ref_without_sb.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/ref_without_sb.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..158f187f157
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/ref_without_sb.main.DataflowConstProp.diff
@@ -0,0 +1,55 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/ref_without_sb.rs:+0:11: +0:11
+      let mut _1: i32;                     // in scope 0 at $DIR/ref_without_sb.rs:+1:9: +1:14
+      let _2: ();                          // in scope 0 at $DIR/ref_without_sb.rs:+2:5: +2:15
+      let mut _3: &i32;                    // in scope 0 at $DIR/ref_without_sb.rs:+2:12: +2:14
+      let _4: &i32;                        // in scope 0 at $DIR/ref_without_sb.rs:+2:12: +2:14
+      let _5: ();                          // in scope 0 at $DIR/ref_without_sb.rs:+4:5: +4:20
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/ref_without_sb.rs:+1:9: +1:14
+          let _6: i32;                     // in scope 1 at $DIR/ref_without_sb.rs:+6:9: +6:10
+          scope 2 {
+              debug b => _6;               // in scope 2 at $DIR/ref_without_sb.rs:+6:9: +6:10
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/ref_without_sb.rs:+1:9: +1:14
+          _1 = const 0_i32;                // scope 0 at $DIR/ref_without_sb.rs:+1:17: +1:18
+          StorageLive(_2);                 // scope 1 at $DIR/ref_without_sb.rs:+2:5: +2:15
+          StorageLive(_3);                 // scope 1 at $DIR/ref_without_sb.rs:+2:12: +2:14
+          StorageLive(_4);                 // scope 1 at $DIR/ref_without_sb.rs:+2:12: +2:14
+          _4 = &_1;                        // scope 1 at $DIR/ref_without_sb.rs:+2:12: +2:14
+          _3 = &(*_4);                     // scope 1 at $DIR/ref_without_sb.rs:+2:12: +2:14
+          _2 = escape::<i32>(move _3) -> bb1; // scope 1 at $DIR/ref_without_sb.rs:+2:5: +2:15
+                                           // mir::Constant
+                                           // + span: $DIR/ref_without_sb.rs:12:5: 12:11
+                                           // + literal: Const { ty: for<'a> fn(&'a i32) {escape::<i32>}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+          StorageDead(_3);                 // scope 1 at $DIR/ref_without_sb.rs:+2:14: +2:15
+          StorageDead(_4);                 // scope 1 at $DIR/ref_without_sb.rs:+2:15: +2:16
+          StorageDead(_2);                 // scope 1 at $DIR/ref_without_sb.rs:+2:15: +2:16
+          _1 = const 1_i32;                // scope 1 at $DIR/ref_without_sb.rs:+3:5: +3:10
+          StorageLive(_5);                 // scope 1 at $DIR/ref_without_sb.rs:+4:5: +4:20
+          _5 = some_function() -> bb2;     // scope 1 at $DIR/ref_without_sb.rs:+4:5: +4:20
+                                           // mir::Constant
+                                           // + span: $DIR/ref_without_sb.rs:14:5: 14:18
+                                           // + literal: Const { ty: fn() {some_function}, val: Value(<ZST>) }
+      }
+  
+      bb2: {
+          StorageDead(_5);                 // scope 1 at $DIR/ref_without_sb.rs:+4:20: +4:21
+          StorageLive(_6);                 // scope 1 at $DIR/ref_without_sb.rs:+6:9: +6:10
+          _6 = _1;                         // scope 1 at $DIR/ref_without_sb.rs:+6:13: +6:14
+          _0 = const ();                   // scope 0 at $DIR/ref_without_sb.rs:+0:11: +7:2
+          StorageDead(_6);                 // scope 1 at $DIR/ref_without_sb.rs:+7:1: +7:2
+          StorageDead(_1);                 // scope 0 at $DIR/ref_without_sb.rs:+7:1: +7:2
+          return;                          // scope 0 at $DIR/ref_without_sb.rs:+7:2: +7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/ref_without_sb.rs b/src/test/mir-opt/dataflow-const-prop/ref_without_sb.rs
new file mode 100644
index 00000000000..2fd480b0968
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/ref_without_sb.rs
@@ -0,0 +1,17 @@
+// unit-test: DataflowConstProp
+
+#[inline(never)]
+fn escape<T>(x: &T) {}
+
+#[inline(never)]
+fn some_function() {}
+
+// EMIT_MIR ref_without_sb.main.DataflowConstProp.diff
+fn main() {
+    let mut a = 0;
+    escape(&a);
+    a = 1;
+    some_function();
+    // This should currently not be propagated.
+    let b = a;
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..f66b00a9a22
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
@@ -0,0 +1,44 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/repr_transparent.rs:+0:11: +0:11
+      let _1: I32;                         // in scope 0 at $DIR/repr_transparent.rs:+1:9: +1:10
+      let mut _3: i32;                     // in scope 0 at $DIR/repr_transparent.rs:+2:17: +2:26
+      let mut _4: i32;                     // in scope 0 at $DIR/repr_transparent.rs:+2:17: +2:20
+      let mut _5: i32;                     // in scope 0 at $DIR/repr_transparent.rs:+2:23: +2:26
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/repr_transparent.rs:+1:9: +1:10
+          let _2: I32;                     // in scope 1 at $DIR/repr_transparent.rs:+2:9: +2:10
+          scope 2 {
+              debug y => _2;               // in scope 2 at $DIR/repr_transparent.rs:+2:9: +2:10
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/repr_transparent.rs:+1:9: +1:10
+          Deinit(_1);                      // scope 0 at $DIR/repr_transparent.rs:+1:13: +1:19
+          (_1.0: i32) = const 0_i32;       // scope 0 at $DIR/repr_transparent.rs:+1:13: +1:19
+          StorageLive(_2);                 // scope 1 at $DIR/repr_transparent.rs:+2:9: +2:10
+          StorageLive(_3);                 // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:26
+          StorageLive(_4);                 // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:20
+-         _4 = (_1.0: i32);                // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:20
++         _4 = const 0_i32;                // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:20
+          StorageLive(_5);                 // scope 1 at $DIR/repr_transparent.rs:+2:23: +2:26
+-         _5 = (_1.0: i32);                // scope 1 at $DIR/repr_transparent.rs:+2:23: +2:26
+-         _3 = Add(move _4, move _5);      // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:26
++         _5 = const 0_i32;                // scope 1 at $DIR/repr_transparent.rs:+2:23: +2:26
++         _3 = const 0_i32;                // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:26
+          StorageDead(_5);                 // scope 1 at $DIR/repr_transparent.rs:+2:25: +2:26
+          StorageDead(_4);                 // scope 1 at $DIR/repr_transparent.rs:+2:25: +2:26
+          Deinit(_2);                      // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
+-         (_2.0: i32) = move _3;           // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
++         (_2.0: i32) = const 0_i32;       // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
+          StorageDead(_3);                 // scope 1 at $DIR/repr_transparent.rs:+2:26: +2:27
+          _0 = const ();                   // scope 0 at $DIR/repr_transparent.rs:+0:11: +3:2
+          StorageDead(_2);                 // scope 1 at $DIR/repr_transparent.rs:+3:1: +3:2
+          StorageDead(_1);                 // scope 0 at $DIR/repr_transparent.rs:+3:1: +3:2
+          return;                          // scope 0 at $DIR/repr_transparent.rs:+3:2: +3:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/repr_transparent.rs b/src/test/mir-opt/dataflow-const-prop/repr_transparent.rs
new file mode 100644
index 00000000000..4ce0ca4dff4
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/repr_transparent.rs
@@ -0,0 +1,12 @@
+// unit-test: DataflowConstProp
+
+// The struct has scalar ABI, but is not a scalar type.
+// Make sure that we handle this correctly.
+#[repr(transparent)]
+struct I32(i32);
+
+// EMIT_MIR repr_transparent.main.DataflowConstProp.diff
+fn main() {
+    let x = I32(0);
+    let y = I32(x.0 + x.0);
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/self_assign.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/self_assign.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..df08eff94cb
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/self_assign.main.DataflowConstProp.diff
@@ -0,0 +1,46 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/self_assign.rs:+0:11: +0:11
+      let mut _1: i32;                     // in scope 0 at $DIR/self_assign.rs:+1:9: +1:14
+      let mut _2: i32;                     // in scope 0 at $DIR/self_assign.rs:+2:9: +2:10
+      let mut _3: i32;                     // in scope 0 at $DIR/self_assign.rs:+3:9: +3:10
+      let mut _5: &i32;                    // in scope 0 at $DIR/self_assign.rs:+6:9: +6:10
+      let mut _6: i32;                     // in scope 0 at $DIR/self_assign.rs:+7:9: +7:11
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/self_assign.rs:+1:9: +1:14
+          let mut _4: &i32;                // in scope 1 at $DIR/self_assign.rs:+5:9: +5:14
+          scope 2 {
+              debug b => _4;               // in scope 2 at $DIR/self_assign.rs:+5:9: +5:14
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/self_assign.rs:+1:9: +1:14
+          _1 = const 0_i32;                // scope 0 at $DIR/self_assign.rs:+1:17: +1:18
+          StorageLive(_2);                 // scope 1 at $DIR/self_assign.rs:+2:9: +2:10
+          _2 = _1;                         // scope 1 at $DIR/self_assign.rs:+2:9: +2:10
+          _1 = Add(move _2, const 1_i32);  // scope 1 at $DIR/self_assign.rs:+2:5: +2:14
+          StorageDead(_2);                 // scope 1 at $DIR/self_assign.rs:+2:13: +2:14
+          StorageLive(_3);                 // scope 1 at $DIR/self_assign.rs:+3:9: +3:10
+          _3 = _1;                         // scope 1 at $DIR/self_assign.rs:+3:9: +3:10
+          _1 = move _3;                    // scope 1 at $DIR/self_assign.rs:+3:5: +3:10
+          StorageDead(_3);                 // scope 1 at $DIR/self_assign.rs:+3:9: +3:10
+          StorageLive(_4);                 // scope 1 at $DIR/self_assign.rs:+5:9: +5:14
+          _4 = &_1;                        // scope 1 at $DIR/self_assign.rs:+5:17: +5:19
+          StorageLive(_5);                 // scope 2 at $DIR/self_assign.rs:+6:9: +6:10
+          _5 = _4;                         // scope 2 at $DIR/self_assign.rs:+6:9: +6:10
+          _4 = move _5;                    // scope 2 at $DIR/self_assign.rs:+6:5: +6:10
+          StorageDead(_5);                 // scope 2 at $DIR/self_assign.rs:+6:9: +6:10
+          StorageLive(_6);                 // scope 2 at $DIR/self_assign.rs:+7:9: +7:11
+          _6 = (*_4);                      // scope 2 at $DIR/self_assign.rs:+7:9: +7:11
+          _1 = move _6;                    // scope 2 at $DIR/self_assign.rs:+7:5: +7:11
+          StorageDead(_6);                 // scope 2 at $DIR/self_assign.rs:+7:10: +7:11
+          _0 = const ();                   // scope 0 at $DIR/self_assign.rs:+0:11: +8:2
+          StorageDead(_4);                 // scope 1 at $DIR/self_assign.rs:+8:1: +8:2
+          StorageDead(_1);                 // scope 0 at $DIR/self_assign.rs:+8:1: +8:2
+          return;                          // scope 0 at $DIR/self_assign.rs:+8:2: +8:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/self_assign.rs b/src/test/mir-opt/dataflow-const-prop/self_assign.rs
new file mode 100644
index 00000000000..8de2195f93b
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/self_assign.rs
@@ -0,0 +1,12 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR self_assign.main.DataflowConstProp.diff
+fn main() {
+    let mut a = 0;
+    a = a + 1;
+    a = a;
+
+    let mut b = &a;
+    b = b;
+    a = *b;
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/self_assign_add.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/self_assign_add.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..c09e4061ede
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/self_assign_add.main.DataflowConstProp.diff
@@ -0,0 +1,23 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/self_assign_add.rs:+0:11: +0:11
+      let mut _1: i32;                     // in scope 0 at $DIR/self_assign_add.rs:+1:9: +1:14
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/self_assign_add.rs:+1:9: +1:14
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/self_assign_add.rs:+1:9: +1:14
+          _1 = const 0_i32;                // scope 0 at $DIR/self_assign_add.rs:+1:17: +1:18
+-         _1 = Add(_1, const 1_i32);       // scope 1 at $DIR/self_assign_add.rs:+2:5: +2:11
+-         _1 = Add(_1, const 1_i32);       // scope 1 at $DIR/self_assign_add.rs:+3:5: +3:11
++         _1 = const 1_i32;                // scope 1 at $DIR/self_assign_add.rs:+2:5: +2:11
++         _1 = const 2_i32;                // scope 1 at $DIR/self_assign_add.rs:+3:5: +3:11
+          _0 = const ();                   // scope 0 at $DIR/self_assign_add.rs:+0:11: +4:2
+          StorageDead(_1);                 // scope 0 at $DIR/self_assign_add.rs:+4:1: +4:2
+          return;                          // scope 0 at $DIR/self_assign_add.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/self_assign_add.rs b/src/test/mir-opt/dataflow-const-prop/self_assign_add.rs
new file mode 100644
index 00000000000..e3282762459
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/self_assign_add.rs
@@ -0,0 +1,8 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR self_assign_add.main.DataflowConstProp.diff
+fn main() {
+    let mut a = 0;
+    a += 1;
+    a += 1;
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/sibling_ptr.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/sibling_ptr.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..8126d4b8585
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/sibling_ptr.main.DataflowConstProp.diff
@@ -0,0 +1,56 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/sibling_ptr.rs:+0:11: +0:11
+      let mut _1: (u8, u8);                // in scope 0 at $DIR/sibling_ptr.rs:+1:9: +1:14
+      let _2: ();                          // in scope 0 at $DIR/sibling_ptr.rs:+2:5: +5:6
+      let mut _4: *mut u8;                 // in scope 0 at $DIR/sibling_ptr.rs:+4:10: +4:18
+      let mut _5: *mut u8;                 // in scope 0 at $DIR/sibling_ptr.rs:+4:10: +4:11
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/sibling_ptr.rs:+1:9: +1:14
+          let _6: u8;                      // in scope 1 at $DIR/sibling_ptr.rs:+6:9: +6:11
+          scope 2 {
+              let _3: *mut u8;             // in scope 2 at $DIR/sibling_ptr.rs:+3:13: +3:14
+              scope 3 {
+                  debug p => _3;           // in scope 3 at $DIR/sibling_ptr.rs:+3:13: +3:14
+              }
+          }
+          scope 4 {
+              debug x1 => _6;              // in scope 4 at $DIR/sibling_ptr.rs:+6:9: +6:11
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/sibling_ptr.rs:+1:9: +1:14
+          Deinit(_1);                      // scope 0 at $DIR/sibling_ptr.rs:+1:27: +1:33
+          (_1.0: u8) = const 0_u8;         // scope 0 at $DIR/sibling_ptr.rs:+1:27: +1:33
+          (_1.1: u8) = const 0_u8;         // scope 0 at $DIR/sibling_ptr.rs:+1:27: +1:33
+          StorageLive(_2);                 // scope 1 at $DIR/sibling_ptr.rs:+2:5: +5:6
+          StorageLive(_3);                 // scope 2 at $DIR/sibling_ptr.rs:+3:13: +3:14
+          _3 = &raw mut (_1.0: u8);        // scope 2 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+          StorageLive(_4);                 // scope 3 at $DIR/sibling_ptr.rs:+4:10: +4:18
+          StorageLive(_5);                 // scope 3 at $DIR/sibling_ptr.rs:+4:10: +4:11
+          _5 = _3;                         // scope 3 at $DIR/sibling_ptr.rs:+4:10: +4:11
+          _4 = ptr::mut_ptr::<impl *mut u8>::add(move _5, const 1_usize) -> bb1; // scope 3 at $DIR/sibling_ptr.rs:+4:10: +4:18
+                                           // mir::Constant
+                                           // + span: $DIR/sibling_ptr.rs:8:12: 8:15
+                                           // + literal: Const { ty: unsafe fn(*mut u8, usize) -> *mut u8 {ptr::mut_ptr::<impl *mut u8>::add}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+          StorageDead(_5);                 // scope 3 at $DIR/sibling_ptr.rs:+4:17: +4:18
+          (*_4) = const 1_u8;              // scope 3 at $DIR/sibling_ptr.rs:+4:9: +4:22
+          StorageDead(_4);                 // scope 3 at $DIR/sibling_ptr.rs:+4:22: +4:23
+          _2 = const ();                   // scope 2 at $DIR/sibling_ptr.rs:+2:5: +5:6
+          StorageDead(_3);                 // scope 2 at $DIR/sibling_ptr.rs:+5:5: +5:6
+          StorageDead(_2);                 // scope 1 at $DIR/sibling_ptr.rs:+5:5: +5:6
+          StorageLive(_6);                 // scope 1 at $DIR/sibling_ptr.rs:+6:9: +6:11
+          _6 = (_1.1: u8);                 // scope 1 at $DIR/sibling_ptr.rs:+6:14: +6:17
+          _0 = const ();                   // scope 0 at $DIR/sibling_ptr.rs:+0:11: +7:2
+          StorageDead(_6);                 // scope 1 at $DIR/sibling_ptr.rs:+7:1: +7:2
+          StorageDead(_1);                 // scope 0 at $DIR/sibling_ptr.rs:+7:1: +7:2
+          return;                          // scope 0 at $DIR/sibling_ptr.rs:+7:2: +7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/sibling_ptr.rs b/src/test/mir-opt/dataflow-const-prop/sibling_ptr.rs
new file mode 100644
index 00000000000..87ef00d1829
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/sibling_ptr.rs
@@ -0,0 +1,11 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR sibling_ptr.main.DataflowConstProp.diff
+fn main() {
+    let mut x: (u8, u8) = (0, 0);
+    unsafe {
+        let p = std::ptr::addr_of_mut!(x.0);
+        *p.add(1) = 1;
+    }
+    let x1 = x.1;  // should not be propagated
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..cfb2706c167
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.diff
@@ -0,0 +1,52 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/struct.rs:+0:11: +0:11
+      let mut _1: S;                       // in scope 0 at $DIR/struct.rs:+1:9: +1:14
+      let mut _3: i32;                     // in scope 0 at $DIR/struct.rs:+2:13: +2:16
+      let mut _5: i32;                     // in scope 0 at $DIR/struct.rs:+4:13: +4:14
+      let mut _6: i32;                     // in scope 0 at $DIR/struct.rs:+4:17: +4:20
+      scope 1 {
+          debug s => _1;                   // in scope 1 at $DIR/struct.rs:+1:9: +1:14
+          let _2: i32;                     // in scope 1 at $DIR/struct.rs:+2:9: +2:10
+          scope 2 {
+              debug a => _2;               // in scope 2 at $DIR/struct.rs:+2:9: +2:10
+              let _4: i32;                 // in scope 2 at $DIR/struct.rs:+4:9: +4:10
+              scope 3 {
+                  debug b => _4;           // in scope 3 at $DIR/struct.rs:+4:9: +4:10
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/struct.rs:+1:9: +1:14
+          Deinit(_1);                      // scope 0 at $DIR/struct.rs:+1:17: +1:21
+          (_1.0: i32) = const 1_i32;       // scope 0 at $DIR/struct.rs:+1:17: +1:21
+          StorageLive(_2);                 // scope 1 at $DIR/struct.rs:+2:9: +2:10
+          StorageLive(_3);                 // scope 1 at $DIR/struct.rs:+2:13: +2:16
+-         _3 = (_1.0: i32);                // scope 1 at $DIR/struct.rs:+2:13: +2:16
+-         _2 = Add(move _3, const 2_i32);  // scope 1 at $DIR/struct.rs:+2:13: +2:20
++         _3 = const 1_i32;                // scope 1 at $DIR/struct.rs:+2:13: +2:16
++         _2 = const 3_i32;                // scope 1 at $DIR/struct.rs:+2:13: +2:20
+          StorageDead(_3);                 // scope 1 at $DIR/struct.rs:+2:19: +2:20
+          (_1.0: i32) = const 3_i32;       // scope 2 at $DIR/struct.rs:+3:5: +3:12
+          StorageLive(_4);                 // scope 2 at $DIR/struct.rs:+4:9: +4:10
+          StorageLive(_5);                 // scope 2 at $DIR/struct.rs:+4:13: +4:14
+-         _5 = _2;                         // scope 2 at $DIR/struct.rs:+4:13: +4:14
++         _5 = const 3_i32;                // scope 2 at $DIR/struct.rs:+4:13: +4:14
+          StorageLive(_6);                 // scope 2 at $DIR/struct.rs:+4:17: +4:20
+-         _6 = (_1.0: i32);                // scope 2 at $DIR/struct.rs:+4:17: +4:20
+-         _4 = Add(move _5, move _6);      // scope 2 at $DIR/struct.rs:+4:13: +4:20
++         _6 = const 3_i32;                // scope 2 at $DIR/struct.rs:+4:17: +4:20
++         _4 = const 6_i32;                // scope 2 at $DIR/struct.rs:+4:13: +4:20
+          StorageDead(_6);                 // scope 2 at $DIR/struct.rs:+4:19: +4:20
+          StorageDead(_5);                 // scope 2 at $DIR/struct.rs:+4:19: +4:20
+          _0 = const ();                   // scope 0 at $DIR/struct.rs:+0:11: +5:2
+          StorageDead(_4);                 // scope 2 at $DIR/struct.rs:+5:1: +5:2
+          StorageDead(_2);                 // scope 1 at $DIR/struct.rs:+5:1: +5:2
+          StorageDead(_1);                 // scope 0 at $DIR/struct.rs:+5:1: +5:2
+          return;                          // scope 0 at $DIR/struct.rs:+5:2: +5:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/struct.rs b/src/test/mir-opt/dataflow-const-prop/struct.rs
new file mode 100644
index 00000000000..841b279e03e
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/struct.rs
@@ -0,0 +1,11 @@
+// unit-test: DataflowConstProp
+
+struct S(i32);
+
+// EMIT_MIR struct.main.DataflowConstProp.diff
+fn main() {
+    let mut s = S(1);
+    let a = s.0 + 2;
+    s.0 = 3;
+    let b = a + s.0;
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/terminator.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/terminator.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..8018400e798
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/terminator.main.DataflowConstProp.diff
@@ -0,0 +1,40 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/terminator.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/terminator.rs:+1:9: +1:10
+      let _2: ();                          // in scope 0 at $DIR/terminator.rs:+3:5: +3:15
+      let mut _3: i32;                     // in scope 0 at $DIR/terminator.rs:+3:9: +3:14
+      let mut _4: i32;                     // in scope 0 at $DIR/terminator.rs:+3:9: +3:10
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/terminator.rs:+1:9: +1:10
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/terminator.rs:+1:9: +1:10
+          _1 = const 1_i32;                // scope 0 at $DIR/terminator.rs:+1:13: +1:14
+          StorageLive(_2);                 // scope 1 at $DIR/terminator.rs:+3:5: +3:15
+          StorageLive(_3);                 // scope 1 at $DIR/terminator.rs:+3:9: +3:14
+          StorageLive(_4);                 // scope 1 at $DIR/terminator.rs:+3:9: +3:10
+-         _4 = _1;                         // scope 1 at $DIR/terminator.rs:+3:9: +3:10
+-         _3 = Add(move _4, const 1_i32);  // scope 1 at $DIR/terminator.rs:+3:9: +3:14
++         _4 = const 1_i32;                // scope 1 at $DIR/terminator.rs:+3:9: +3:10
++         _3 = const 2_i32;                // scope 1 at $DIR/terminator.rs:+3:9: +3:14
+          StorageDead(_4);                 // scope 1 at $DIR/terminator.rs:+3:13: +3:14
+-         _2 = foo(move _3) -> bb1;        // scope 1 at $DIR/terminator.rs:+3:5: +3:15
++         _2 = foo(const 2_i32) -> bb1;    // scope 1 at $DIR/terminator.rs:+3:5: +3:15
+                                           // mir::Constant
+                                           // + span: $DIR/terminator.rs:9:5: 9:8
+                                           // + literal: Const { ty: fn(i32) {foo}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+          StorageDead(_3);                 // scope 1 at $DIR/terminator.rs:+3:14: +3:15
+          StorageDead(_2);                 // scope 1 at $DIR/terminator.rs:+3:15: +3:16
+          _0 = const ();                   // scope 0 at $DIR/terminator.rs:+0:11: +4:2
+          StorageDead(_1);                 // scope 0 at $DIR/terminator.rs:+4:1: +4:2
+          return;                          // scope 0 at $DIR/terminator.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/terminator.rs b/src/test/mir-opt/dataflow-const-prop/terminator.rs
new file mode 100644
index 00000000000..d151f666a2d
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/terminator.rs
@@ -0,0 +1,10 @@
+// unit-test: DataflowConstProp
+
+fn foo(n: i32) {}
+
+// EMIT_MIR terminator.main.DataflowConstProp.diff
+fn main() {
+    let a = 1;
+    // Checks that we propagate into terminators.
+    foo(a + 1);
+}
diff --git a/src/test/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.diff b/src/test/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.diff
new file mode 100644
index 00000000000..e028def00a1
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.diff
@@ -0,0 +1,75 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/tuple.rs:+0:11: +0:11
+      let mut _1: (i32, i32);              // in scope 0 at $DIR/tuple.rs:+1:9: +1:14
+      let mut _3: i32;                     // in scope 0 at $DIR/tuple.rs:+2:13: +2:22
+      let mut _4: i32;                     // in scope 0 at $DIR/tuple.rs:+2:13: +2:16
+      let mut _5: i32;                     // in scope 0 at $DIR/tuple.rs:+2:19: +2:22
+      let mut _7: i32;                     // in scope 0 at $DIR/tuple.rs:+4:13: +4:22
+      let mut _8: i32;                     // in scope 0 at $DIR/tuple.rs:+4:13: +4:16
+      let mut _9: i32;                     // in scope 0 at $DIR/tuple.rs:+4:19: +4:22
+      let mut _10: i32;                    // in scope 0 at $DIR/tuple.rs:+4:25: +4:26
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/tuple.rs:+1:9: +1:14
+          let _2: i32;                     // in scope 1 at $DIR/tuple.rs:+2:9: +2:10
+          scope 2 {
+              debug b => _2;               // in scope 2 at $DIR/tuple.rs:+2:9: +2:10
+              let _6: i32;                 // in scope 2 at $DIR/tuple.rs:+4:9: +4:10
+              scope 3 {
+                  debug c => _6;           // in scope 3 at $DIR/tuple.rs:+4:9: +4:10
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/tuple.rs:+1:9: +1:14
+          Deinit(_1);                      // scope 0 at $DIR/tuple.rs:+1:17: +1:23
+          (_1.0: i32) = const 1_i32;       // scope 0 at $DIR/tuple.rs:+1:17: +1:23
+          (_1.1: i32) = const 2_i32;       // scope 0 at $DIR/tuple.rs:+1:17: +1:23
+          StorageLive(_2);                 // scope 1 at $DIR/tuple.rs:+2:9: +2:10
+          StorageLive(_3);                 // scope 1 at $DIR/tuple.rs:+2:13: +2:22
+          StorageLive(_4);                 // scope 1 at $DIR/tuple.rs:+2:13: +2:16
+-         _4 = (_1.0: i32);                // scope 1 at $DIR/tuple.rs:+2:13: +2:16
++         _4 = const 1_i32;                // scope 1 at $DIR/tuple.rs:+2:13: +2:16
+          StorageLive(_5);                 // scope 1 at $DIR/tuple.rs:+2:19: +2:22
+-         _5 = (_1.1: i32);                // scope 1 at $DIR/tuple.rs:+2:19: +2:22
+-         _3 = Add(move _4, move _5);      // scope 1 at $DIR/tuple.rs:+2:13: +2:22
++         _5 = const 2_i32;                // scope 1 at $DIR/tuple.rs:+2:19: +2:22
++         _3 = const 3_i32;                // scope 1 at $DIR/tuple.rs:+2:13: +2:22
+          StorageDead(_5);                 // scope 1 at $DIR/tuple.rs:+2:21: +2:22
+          StorageDead(_4);                 // scope 1 at $DIR/tuple.rs:+2:21: +2:22
+-         _2 = Add(move _3, const 3_i32);  // scope 1 at $DIR/tuple.rs:+2:13: +2:26
++         _2 = const 6_i32;                // scope 1 at $DIR/tuple.rs:+2:13: +2:26
+          StorageDead(_3);                 // scope 1 at $DIR/tuple.rs:+2:25: +2:26
+          Deinit(_1);                      // scope 2 at $DIR/tuple.rs:+3:5: +3:15
+          (_1.0: i32) = const 2_i32;       // scope 2 at $DIR/tuple.rs:+3:5: +3:15
+          (_1.1: i32) = const 3_i32;       // scope 2 at $DIR/tuple.rs:+3:5: +3:15
+          StorageLive(_6);                 // scope 2 at $DIR/tuple.rs:+4:9: +4:10
+          StorageLive(_7);                 // scope 2 at $DIR/tuple.rs:+4:13: +4:22
+          StorageLive(_8);                 // scope 2 at $DIR/tuple.rs:+4:13: +4:16
+-         _8 = (_1.0: i32);                // scope 2 at $DIR/tuple.rs:+4:13: +4:16
++         _8 = const 2_i32;                // scope 2 at $DIR/tuple.rs:+4:13: +4:16
+          StorageLive(_9);                 // scope 2 at $DIR/tuple.rs:+4:19: +4:22
+-         _9 = (_1.1: i32);                // scope 2 at $DIR/tuple.rs:+4:19: +4:22
+-         _7 = Add(move _8, move _9);      // scope 2 at $DIR/tuple.rs:+4:13: +4:22
++         _9 = const 3_i32;                // scope 2 at $DIR/tuple.rs:+4:19: +4:22
++         _7 = const 5_i32;                // scope 2 at $DIR/tuple.rs:+4:13: +4:22
+          StorageDead(_9);                 // scope 2 at $DIR/tuple.rs:+4:21: +4:22
+          StorageDead(_8);                 // scope 2 at $DIR/tuple.rs:+4:21: +4:22
+          StorageLive(_10);                // scope 2 at $DIR/tuple.rs:+4:25: +4:26
+-         _10 = _2;                        // scope 2 at $DIR/tuple.rs:+4:25: +4:26
+-         _6 = Add(move _7, move _10);     // scope 2 at $DIR/tuple.rs:+4:13: +4:26
++         _10 = const 6_i32;               // scope 2 at $DIR/tuple.rs:+4:25: +4:26
++         _6 = const 11_i32;               // scope 2 at $DIR/tuple.rs:+4:13: +4:26
+          StorageDead(_10);                // scope 2 at $DIR/tuple.rs:+4:25: +4:26
+          StorageDead(_7);                 // scope 2 at $DIR/tuple.rs:+4:25: +4:26
+          _0 = const ();                   // scope 0 at $DIR/tuple.rs:+0:11: +5:2
+          StorageDead(_6);                 // scope 2 at $DIR/tuple.rs:+5:1: +5:2
+          StorageDead(_2);                 // scope 1 at $DIR/tuple.rs:+5:1: +5:2
+          StorageDead(_1);                 // scope 0 at $DIR/tuple.rs:+5:1: +5:2
+          return;                          // scope 0 at $DIR/tuple.rs:+5:2: +5:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/dataflow-const-prop/tuple.rs b/src/test/mir-opt/dataflow-const-prop/tuple.rs
new file mode 100644
index 00000000000..92c70eab0ff
--- /dev/null
+++ b/src/test/mir-opt/dataflow-const-prop/tuple.rs
@@ -0,0 +1,9 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR tuple.main.DataflowConstProp.diff
+fn main() {
+    let mut a = (1, 2);
+    let b = a.0 + a.1 + 3;
+    a = (2, 3);
+    let c = a.0 + a.1 + b;
+}
diff --git a/src/test/mir-opt/issue_101973.inner.ConstProp.diff b/src/test/mir-opt/issue_101973.inner.ConstProp.diff
index c24abedae92..bce40f277d2 100644
--- a/src/test/mir-opt/issue_101973.inner.ConstProp.diff
+++ b/src/test/mir-opt/issue_101973.inner.ConstProp.diff
@@ -37,7 +37,6 @@
           StorageLive(_4);                 // scope 0 at $DIR/issue_101973.rs:+1:5: +1:17
           StorageLive(_5);                 // scope 0 at $DIR/issue_101973.rs:+1:10: +1:16
           _5 = _1;                         // scope 0 at $DIR/issue_101973.rs:+1:10: +1:16
-          _4 = const 0_u32;                // scope 1 at $DIR/issue_101973.rs:6:19: 6:23
           StorageLive(_12);                // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
           StorageLive(_13);                // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
           StorageLive(_14);                // scope 2 at $DIR/issue_101973.rs:7:13: 7:14
@@ -73,7 +72,7 @@
           StorageDead(_14);                // scope 2 at $DIR/issue_101973.rs:7:19: 7:20
           _12 = BitAnd(move _13, const 255_u32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
           StorageDead(_13);                // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
-          _4 = BitOr(_4, move _12);        // scope 2 at $DIR/issue_101973.rs:7:5: 7:27
+          _4 = BitOr(const 0_u32, move _12); // scope 2 at $DIR/issue_101973.rs:7:5: 7:27
           StorageDead(_12);                // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
           StorageDead(_5);                 // scope 0 at $DIR/issue_101973.rs:+1:16: +1:17
           StorageLive(_6);                 // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
diff --git a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff
index c9a9511586d..b88cdfcbc96 100644
--- a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff
+++ b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff
@@ -26,6 +26,8 @@
       let mut _25: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       let _26: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       let mut _27: std::option::Option<std::fmt::Arguments<'_>>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _29: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _30: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       scope 1 {
           debug split => _1;               // in scope 1 at $DIR/issue_73223.rs:+1:9: +1:14
           let _6: std::option::Option<i32>; // in scope 1 at $DIR/issue_73223.rs:+6:9: +6:14
@@ -83,7 +85,8 @@
           discriminant(_6) = 1;            // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28
           StorageDead(_7);                 // scope 1 at $DIR/issue_73223.rs:+6:27: +6:28
           StorageLive(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_29);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_30);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           _10 = &_1;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_11);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
@@ -92,15 +95,16 @@
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
           _11 = _28;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          Deinit(_9);                      // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          (_9.0: &i32) = move _10;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          (_9.1: &i32) = move _11;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          Deinit(_29);                     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          Deinit(_30);                     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _29 = move _10;                  // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _30 = move _11;                  // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_11);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_13);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _13 = (_9.0: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _13 = _29;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_14);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _14 = (_9.1: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _14 = _30;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_15);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_16);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_17);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
@@ -150,7 +154,8 @@
           StorageDead(_15);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_14);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_13);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageDead(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_29);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_30);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           nop;                             // scope 0 at $DIR/issue_73223.rs:+0:11: +8:2
           StorageDead(_6);                 // scope 1 at $DIR/issue_73223.rs:+8:1: +8:2
diff --git a/src/test/mir-opt/lower_intrinsics_e2e.f_u64.PreCodegen.after.mir b/src/test/mir-opt/lower_intrinsics_e2e.f_u64.PreCodegen.after.mir
index 8e185323e1a..f6d8bdd7422 100644
--- a/src/test/mir-opt/lower_intrinsics_e2e.f_u64.PreCodegen.after.mir
+++ b/src/test/mir-opt/lower_intrinsics_e2e.f_u64.PreCodegen.after.mir
@@ -6,25 +6,20 @@ fn f_u64() -> () {
     scope 1 (inlined f_dispatch::<u64>) { // at $DIR/lower_intrinsics_e2e.rs:15:5: 15:21
         debug t => _1;                   // in scope 1 at $DIR/lower_intrinsics_e2e.rs:19:22: 19:23
         let _2: ();                      // in scope 1 at $DIR/lower_intrinsics_e2e.rs:23:9: 23:21
-        let mut _3: u64;                 // in scope 1 at $DIR/lower_intrinsics_e2e.rs:23:19: 23:20
         scope 2 (inlined std::mem::size_of::<u64>) { // at $DIR/lower_intrinsics_e2e.rs:20:8: 20:32
         }
     }
 
     bb0: {
         StorageLive(_1);                 // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:5: +1:21
-        _1 = const 0_u64;                // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:5: +1:21
         StorageLive(_2);                 // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:9: 23:21
-        StorageLive(_3);                 // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:19: 23:20
-        _3 = move _1;                    // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:19: 23:20
-        _2 = f_non_zst::<u64>(move _3) -> bb1; // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:9: 23:21
+        _2 = f_non_zst::<u64>(const 0_u64) -> bb1; // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:9: 23:21
                                          // mir::Constant
                                          // + span: $DIR/lower_intrinsics_e2e.rs:23:9: 23:18
                                          // + literal: Const { ty: fn(u64) {f_non_zst::<u64>}, val: Value(<ZST>) }
     }
 
     bb1: {
-        StorageDead(_3);                 // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:20: 23:21
         StorageDead(_2);                 // scope 1 at $DIR/lower_intrinsics_e2e.rs:23:21: 23:22
         StorageDead(_1);                 // scope 0 at $DIR/lower_intrinsics_e2e.rs:+1:5: +1:21
         return;                          // scope 0 at $DIR/lower_intrinsics_e2e.rs:+2:2: +2:2
diff --git a/src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..eb88304466e
--- /dev/null
+++ b/src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,50 @@
+- // MIR for `dropping` before ScalarReplacementOfAggregates
++ // MIR for `dropping` after ScalarReplacementOfAggregates
+  
+  fn dropping() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/sroa.rs:+0:19: +0:19
+      let _1: Tag;                         // in scope 0 at $DIR/sroa.rs:+1:5: +1:32
+      let mut _2: S;                       // in scope 0 at $DIR/sroa.rs:+1:5: +1:30
+      let mut _3: Tag;                     // in scope 0 at $DIR/sroa.rs:+1:7: +1:13
+      let mut _4: Tag;                     // in scope 0 at $DIR/sroa.rs:+1:15: +1:21
+      let mut _5: Tag;                     // in scope 0 at $DIR/sroa.rs:+1:23: +1:29
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/sroa.rs:+1:5: +1:32
+          StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+1:7: +1:13
+          Deinit(_3);                      // scope 0 at $DIR/sroa.rs:+1:7: +1:13
+          (_3.0: usize) = const 0_usize;   // scope 0 at $DIR/sroa.rs:+1:7: +1:13
+          StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+1:15: +1:21
+          Deinit(_4);                      // scope 0 at $DIR/sroa.rs:+1:15: +1:21
+          (_4.0: usize) = const 1_usize;   // scope 0 at $DIR/sroa.rs:+1:15: +1:21
+          StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+1:23: +1:29
+          Deinit(_5);                      // scope 0 at $DIR/sroa.rs:+1:23: +1:29
+          (_5.0: usize) = const 2_usize;   // scope 0 at $DIR/sroa.rs:+1:23: +1:29
+          Deinit(_2);                      // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          (_2.0: Tag) = move _3;           // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          (_2.1: Tag) = move _4;           // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          (_2.2: Tag) = move _5;           // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+1:29: +1:30
+          StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+1:29: +1:30
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+1:29: +1:30
+          _1 = move (_2.1: Tag);           // scope 0 at $DIR/sroa.rs:+1:5: +1:32
+          drop(_1) -> bb1;                 // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+      }
+  
+      bb1: {
+          drop((_2.0: Tag)) -> bb3;        // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+      }
+  
+      bb2: {
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+          StorageDead(_1);                 // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+          _0 = const ();                   // scope 0 at $DIR/sroa.rs:+0:19: +2:2
+          return;                          // scope 0 at $DIR/sroa.rs:+2:2: +2:2
+      }
+  
+      bb3: {
+          drop((_2.2: Tag)) -> bb2;        // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..7c7e87c32a2
--- /dev/null
+++ b/src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,45 @@
+- // MIR for `enums` before ScalarReplacementOfAggregates
++ // MIR for `enums` after ScalarReplacementOfAggregates
+  
+  fn enums(_1: usize) -> usize {
+      debug a => _1;                       // in scope 0 at $DIR/sroa.rs:+0:14: +0:15
+      let mut _0: usize;                   // return place in scope 0 at $DIR/sroa.rs:+0:27: +0:32
+      let mut _2: std::option::Option<usize>; // in scope 0 at $DIR/sroa.rs:+1:22: +1:29
+      let mut _3: usize;                   // in scope 0 at $DIR/sroa.rs:+1:27: +1:28
+      let mut _4: isize;                   // in scope 0 at $DIR/sroa.rs:+1:12: +1:19
+      scope 1 {
+          debug a => _5;                   // in scope 1 at $DIR/sroa.rs:+1:17: +1:18
+          let _5: usize;                   // in scope 1 at $DIR/sroa.rs:+1:17: +1:18
+      }
+  
+      bb0: {
+          StorageLive(_2);                 // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          StorageLive(_3);                 // scope 1 at $DIR/sroa.rs:+1:27: +1:28
+          _3 = _1;                         // scope 1 at $DIR/sroa.rs:+1:27: +1:28
+          Deinit(_2);                      // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          ((_2 as Some).0: usize) = move _3; // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          discriminant(_2) = 1;            // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          StorageDead(_3);                 // scope 1 at $DIR/sroa.rs:+1:28: +1:29
+          _4 = discriminant(_2);           // scope 1 at $DIR/sroa.rs:+1:12: +1:19
+          switchInt(move _4) -> [1_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/sroa.rs:+1:12: +1:19
+      }
+  
+      bb1: {
+          StorageLive(_5);                 // scope 1 at $DIR/sroa.rs:+1:17: +1:18
+          _5 = ((_2 as Some).0: usize);    // scope 1 at $DIR/sroa.rs:+1:17: +1:18
+          _0 = _5;                         // scope 1 at $DIR/sroa.rs:+1:32: +1:33
+          StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+1:34: +1:35
+          goto -> bb3;                     // scope 0 at $DIR/sroa.rs:+1:5: +1:46
+      }
+  
+      bb2: {
+          _0 = const 0_usize;              // scope 0 at $DIR/sroa.rs:+1:43: +1:44
+          goto -> bb3;                     // scope 0 at $DIR/sroa.rs:+1:5: +1:46
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+2:1: +2:2
+          return;                          // scope 0 at $DIR/sroa.rs:+2:2: +2:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..64559b58f61
--- /dev/null
+++ b/src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,47 @@
+- // MIR for `escaping` before ScalarReplacementOfAggregates
++ // MIR for `escaping` after ScalarReplacementOfAggregates
+  
+  fn escaping() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/sroa.rs:+0:19: +0:19
+      let _1: ();                          // in scope 0 at $DIR/sroa.rs:+2:5: +2:42
+      let mut _2: *const u32;              // in scope 0 at $DIR/sroa.rs:+2:7: +2:41
+      let _3: &u32;                        // in scope 0 at $DIR/sroa.rs:+2:7: +2:41
+      let _4: Escaping;                    // in scope 0 at $DIR/sroa.rs:+2:8: +2:39
+      let mut _5: u32;                     // in scope 0 at $DIR/sroa.rs:+2:34: +2:37
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/sroa.rs:+2:5: +2:42
+          StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+2:34: +2:37
+          _5 = g() -> bb1;                 // scope 0 at $DIR/sroa.rs:+2:34: +2:37
+                                           // mir::Constant
+                                           // + span: $DIR/sroa.rs:78:34: 78:35
+                                           // + literal: Const { ty: fn() -> u32 {g}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+          Deinit(_4);                      // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          (_4.0: u32) = const 1_u32;       // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          (_4.1: u32) = const 2_u32;       // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          (_4.2: u32) = move _5;           // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+2:38: +2:39
+          _3 = &(_4.0: u32);               // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          _2 = &raw const (*_3);           // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          _1 = f(move _2) -> bb2;          // scope 0 at $DIR/sroa.rs:+2:5: +2:42
+                                           // mir::Constant
+                                           // + span: $DIR/sroa.rs:78:5: 78:6
+                                           // + literal: Const { ty: fn(*const u32) {f}, val: Value(<ZST>) }
+      }
+  
+      bb2: {
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+2:41: +2:42
+          StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+2:42: +2:43
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+2:42: +2:43
+          StorageDead(_1);                 // scope 0 at $DIR/sroa.rs:+2:42: +2:43
+          _0 = const ();                   // scope 0 at $DIR/sroa.rs:+0:19: +3:2
+          return;                          // scope 0 at $DIR/sroa.rs:+3:2: +3:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..d4c04d5e68b
--- /dev/null
+++ b/src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,87 @@
+- // MIR for `flat` before ScalarReplacementOfAggregates
++ // MIR for `flat` after ScalarReplacementOfAggregates
+  
+  fn flat() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/sroa.rs:+0:15: +0:15
+      let _1: u8;                          // in scope 0 at $DIR/sroa.rs:+1:15: +1:16
+      let _2: ();                          // in scope 0 at $DIR/sroa.rs:+1:18: +1:19
+      let _3: &str;                        // in scope 0 at $DIR/sroa.rs:+1:21: +1:22
+      let _4: std::option::Option<isize>;  // in scope 0 at $DIR/sroa.rs:+1:24: +1:25
+      let mut _5: Foo;                     // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
+      let mut _6: ();                      // in scope 0 at $DIR/sroa.rs:+1:45: +1:47
+      let mut _7: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:60: +1:68
++     let mut _8: u8;                      // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++     let mut _9: ();                      // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++     let mut _10: &str;                   // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++     let mut _11: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/sroa.rs:+1:15: +1:16
+          debug b => _2;                   // in scope 1 at $DIR/sroa.rs:+1:18: +1:19
+          debug c => _3;                   // in scope 1 at $DIR/sroa.rs:+1:21: +1:22
+          debug d => _4;                   // in scope 1 at $DIR/sroa.rs:+1:24: +1:25
+          scope 2 {
+              scope 3 {
+                  scope 4 {
+                      scope 5 {
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_8);                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_9);                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_10);                // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_11);                // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+          StorageLive(_6);                 // scope 0 at $DIR/sroa.rs:+1:45: +1:47
+          Deinit(_6);                      // scope 0 at $DIR/sroa.rs:+1:45: +1:47
+          StorageLive(_7);                 // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+          Deinit(_7);                      // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+          ((_7 as Some).0: isize) = const -4_isize; // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+          discriminant(_7) = 1;            // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+-         Deinit(_5);                      // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+-         (_5.0: u8) = const 5_u8;         // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+-         (_5.1: ()) = move _6;            // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+-         (_5.2: &str) = const "a";        // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_8);                      // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_9);                      // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_10);                     // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_11);                     // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _8 = const 5_u8;                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _9 = move _6;                    // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _10 = const "a";                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+                                           // mir::Constant
+                                           // + span: $DIR/sroa.rs:57:52: 57:55
+                                           // + literal: Const { ty: &str, val: Value(Slice(..)) }
+-         (_5.3: std::option::Option<isize>) = move _7; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _11 = move _7;                   // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+          StorageDead(_7);                 // scope 0 at $DIR/sroa.rs:+1:69: +1:70
+          StorageDead(_6);                 // scope 0 at $DIR/sroa.rs:+1:69: +1:70
+          StorageLive(_1);                 // scope 0 at $DIR/sroa.rs:+1:15: +1:16
+-         _1 = (_5.0: u8);                 // scope 0 at $DIR/sroa.rs:+1:15: +1:16
++         _1 = _8;                         // scope 0 at $DIR/sroa.rs:+1:15: +1:16
+          StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+1:18: +1:19
+-         _2 = (_5.1: ());                 // scope 0 at $DIR/sroa.rs:+1:18: +1:19
++         _2 = _9;                         // scope 0 at $DIR/sroa.rs:+1:18: +1:19
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+1:21: +1:22
+-         _3 = (_5.2: &str);               // scope 0 at $DIR/sroa.rs:+1:21: +1:22
++         _3 = _10;                        // scope 0 at $DIR/sroa.rs:+1:21: +1:22
+          StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+1:24: +1:25
+-         _4 = (_5.3: std::option::Option<isize>); // scope 0 at $DIR/sroa.rs:+1:24: +1:25
+-         StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         _4 = _11;                        // scope 0 at $DIR/sroa.rs:+1:24: +1:25
++         StorageDead(_8);                 // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         StorageDead(_9);                 // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         StorageDead(_10);                // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         StorageDead(_11);                // scope 0 at $DIR/sroa.rs:+1:70: +1:71
+          _0 = const ();                   // scope 0 at $DIR/sroa.rs:+0:15: +6:2
+          StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          StorageDead(_1);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          return;                          // scope 0 at $DIR/sroa.rs:+6:2: +6:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.rs b/src/test/mir-opt/sroa.rs
new file mode 100644
index 00000000000..ff8deb40d7d
--- /dev/null
+++ b/src/test/mir-opt/sroa.rs
@@ -0,0 +1,88 @@
+// unit-test: ScalarReplacementOfAggregates
+// compile-flags: -Cpanic=abort
+// no-prefer-dynamic
+
+struct Tag(usize);
+
+#[repr(C)]
+struct S(Tag, Tag, Tag);
+
+impl Drop for Tag {
+    #[inline(never)]
+    fn drop(&mut self) {}
+}
+
+// EMIT_MIR sroa.dropping.ScalarReplacementOfAggregates.diff
+pub fn dropping() {
+    S(Tag(0), Tag(1), Tag(2)).1;
+}
+
+// EMIT_MIR sroa.enums.ScalarReplacementOfAggregates.diff
+pub fn enums(a: usize) -> usize {
+    if let Some(a) = Some(a) { a } else { 0 }
+}
+
+// EMIT_MIR sroa.structs.ScalarReplacementOfAggregates.diff
+pub fn structs(a: f32) -> f32 {
+    struct U {
+        _foo: usize,
+        a: f32,
+    }
+
+    U { _foo: 0, a }.a
+}
+
+// EMIT_MIR sroa.unions.ScalarReplacementOfAggregates.diff
+pub fn unions(a: f32) -> u32 {
+    union Repr {
+        f: f32,
+        u: u32,
+    }
+    unsafe { Repr { f: a }.u }
+}
+
+struct Foo {
+    a: u8,
+    b: (),
+    c: &'static str,
+    d: Option<isize>,
+}
+
+fn g() -> u32 {
+    3
+}
+
+// EMIT_MIR sroa.flat.ScalarReplacementOfAggregates.diff
+pub fn flat() {
+    let Foo { a, b, c, d } = Foo { a: 5, b: (), c: "a", d: Some(-4) };
+    let _ = a;
+    let _ = b;
+    let _ = c;
+    let _ = d;
+}
+
+#[repr(C)]
+struct Escaping {
+    a: u32,
+    b: u32,
+    c: u32,
+}
+
+fn f(a: *const u32) {
+    println!("{}", unsafe { *a.add(2) });
+}
+
+// EMIT_MIR sroa.escaping.ScalarReplacementOfAggregates.diff
+pub fn escaping() {
+    // Verify this struct is not flattened.
+    f(&Escaping { a: 1, b: 2, c: g() }.a);
+}
+
+fn main() {
+    dropping();
+    enums(5);
+    structs(5.);
+    unions(5.);
+    flat();
+    escaping();
+}
diff --git a/src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..69d74c351de
--- /dev/null
+++ b/src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,34 @@
+- // MIR for `structs` before ScalarReplacementOfAggregates
++ // MIR for `structs` after ScalarReplacementOfAggregates
+  
+  fn structs(_1: f32) -> f32 {
+      debug a => _1;                       // in scope 0 at $DIR/sroa.rs:+0:16: +0:17
+      let mut _0: f32;                     // return place in scope 0 at $DIR/sroa.rs:+0:27: +0:30
+      let mut _2: structs::U;              // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
+      let mut _3: f32;                     // in scope 0 at $DIR/sroa.rs:+6:18: +6:19
++     let mut _4: usize;                   // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
++     let mut _5: f32;                     // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
+  
+      bb0: {
+-         StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+6:18: +6:19
+          _3 = _1;                         // scope 0 at $DIR/sroa.rs:+6:18: +6:19
+-         Deinit(_2);                      // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+-         (_2.0: usize) = const 0_usize;   // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+-         (_2.1: f32) = move _3;           // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         Deinit(_4);                      // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         Deinit(_5);                      // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         _4 = const 0_usize;              // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         _5 = move _3;                    // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+6:20: +6:21
+-         _0 = (_2.1: f32);                // scope 0 at $DIR/sroa.rs:+6:5: +6:23
+-         StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+7:1: +7:2
++         _0 = _5;                         // scope 0 at $DIR/sroa.rs:+6:5: +6:23
++         StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+7:1: +7:2
++         StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+7:1: +7:2
+          return;                          // scope 0 at $DIR/sroa.rs:+7:2: +7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..03ca976df7b
--- /dev/null
+++ b/src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,24 @@
+- // MIR for `unions` before ScalarReplacementOfAggregates
++ // MIR for `unions` after ScalarReplacementOfAggregates
+  
+  fn unions(_1: f32) -> u32 {
+      debug a => _1;                       // in scope 0 at $DIR/sroa.rs:+0:15: +0:16
+      let mut _0: u32;                     // return place in scope 0 at $DIR/sroa.rs:+0:26: +0:29
+      let mut _2: unions::Repr;            // in scope 0 at $DIR/sroa.rs:+5:14: +5:27
+      let mut _3: f32;                     // in scope 0 at $DIR/sroa.rs:+5:24: +5:25
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_2);                 // scope 1 at $DIR/sroa.rs:+5:14: +5:27
+          StorageLive(_3);                 // scope 1 at $DIR/sroa.rs:+5:24: +5:25
+          _3 = _1;                         // scope 1 at $DIR/sroa.rs:+5:24: +5:25
+          Deinit(_2);                      // scope 1 at $DIR/sroa.rs:+5:14: +5:27
+          (_2.0: f32) = move _3;           // scope 1 at $DIR/sroa.rs:+5:14: +5:27
+          StorageDead(_3);                 // scope 1 at $DIR/sroa.rs:+5:26: +5:27
+          _0 = (_2.1: u32);                // scope 1 at $DIR/sroa.rs:+5:14: +5:29
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          return;                          // scope 0 at $DIR/sroa.rs:+6:2: +6:2
+      }
+  }
+  
diff --git a/src/test/run-make-fulldeps/split-debuginfo/Makefile b/src/test/run-make-fulldeps/split-debuginfo/Makefile
index 1032f3408f0..1831ab38fab 100644
--- a/src/test/run-make-fulldeps/split-debuginfo/Makefile
+++ b/src/test/run-make-fulldeps/split-debuginfo/Makefile
@@ -3,7 +3,7 @@ include ../tools.mk
 all: off packed unpacked
 
 ifeq ($(UNAME),Darwin)
-# If disabled, don't run dsymutil
+# If disabled, don't run `dsymutil`.
 off:
 	rm -rf $(TMPDIR)/*.dSYM
 	$(RUSTC) foo.rs -g -C split-debuginfo=off
@@ -29,98 +29,280 @@ unpacked:
 	[ ! -d $(TMPDIR)/foo.dSYM ]
 else
 ifdef IS_WINDOWS
-# Windows only supports =packed
+# Windows only supports packed debuginfo - nothing to test.
 off:
 packed:
 unpacked:
 else
+# Some non-Windows, non-Darwin platforms are not stable, and some are.
 ifeq ($(UNAME),Linux)
   UNSTABLEOPTS :=
 else
   UNSTABLEOPTS := -Zunstable-options
 endif
 
+# - Debuginfo in `.o` files
+# - `.o` deleted
+# - `.dwo` never created
+# - `.dwp` never created
 off:
 	$(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off
 	[ ! -f $(TMPDIR)/*.dwp ]
 	[ ! -f $(TMPDIR)/*.dwo ]
-
 	$(RUSTC) foo.rs -g
 	[ ! -f $(TMPDIR)/*.dwp ]
 	[ ! -f $(TMPDIR)/*.dwo ]
 
-packed: packed-split packed-single
+packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate
 
+# - Debuginfo in `.dwo` files
+# - `.o` deleted
+# - `.dwo` deleted
+# - `.dwp` present
 packed-split:
 	$(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split
-	ls $(TMPDIR)/*.dwp
-	rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	rm $(TMPDIR)/foo.dwp
+	rm $(TMPDIR)/$(call BIN,foo)
 
+# - Debuginfo in `.o` files
+# - `.o` deleted
+# - `.dwo` never created
+# - `.dwp` present
 packed-single:
 	$(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single
-	ls $(TMPDIR)/*.dwp
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	rm $(TMPDIR)/foo.dwp
+	rm $(TMPDIR)/$(call BIN,foo)
+
+packed-lto: packed-lto-split packed-lto-single
+
+# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated
+# - `.o` never created
+# - `.dwo` never created
+# - `.dwp` never created
+packed-lto-split:
+	$(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \
+		--crate-type=rlib -Clinker-plugin-lto
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/libbaz.rlib
+
+# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated
+# - `.o` never created
+# - `.dwo` never created
+# - `.dwp` never created
+packed-lto-single:
+	$(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \
+		--crate-type=rlib -Clinker-plugin-lto
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
-	rm -rf $(TMPDIR)/*.dwp
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/libbaz.rlib
 
 packed-remapped: packed-remapped-split packed-remapped-single
 
+# - Debuginfo in `.dwo` files
+# - `.o` and binary refer to remapped `.dwo` paths which do not exist
+# - `.o` deleted
+# - `.dwo` deleted
+# - `.dwp` present
 packed-remapped-split:
 	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \
 		-Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g
 	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	rm $(TMPDIR)/foo.dwp
+	rm $(TMPDIR)/$(call BIN,foo)
 
+# - Debuginfo in `.o` files
+# - `.o` and binary refer to remapped `.o` paths which do not exist
+# - `.o` deleted
+# - `.dwo` never created
+# - `.dwp` present
 packed-remapped-single:
 	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \
 		-Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g
 	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	rm $(TMPDIR)/foo.dwp
+	rm $(TMPDIR)/$(call BIN,foo)
 
 packed-crosscrate: packed-crosscrate-split packed-crosscrate-single
 
+# - Debuginfo in `.dwo` files
+# - (bar) `.rlib` file created, contains `.dwo`
+# - (bar) `.o` deleted
+# - (bar) `.dwo` deleted
+# - (bar) `.dwp` never created
+# - (main) `.o` deleted
+# - (main) `.dwo` deleted
+# - (main) `.dwp` present
 packed-crosscrate-split:
 	$(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \
 		-Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs
 	ls $(TMPDIR)/*.rlib
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
-	$(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib -Z unstable-options $(UNSTABLEOPTS) \
+	$(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \
 		-C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs
-	rm $(TMPDIR)/*.dwo
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
 	rm $(TMPDIR)/main.dwp
 	rm $(TMPDIR)/$(call BIN,main)
 
+# - Debuginfo in `.o` files
+# - (bar) `.rlib` file created, contains `.o`
+# - (bar) `.o` deleted
+# - (bar) `.dwo` never created
+# - (bar) `.dwp` never created
+# - (main) `.o` deleted
+# - (main) `.dwo` never created
+# - (main) `.dwp` present
 packed-crosscrate-single:
 	$(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \
 		-Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs
 	ls $(TMPDIR)/*.rlib
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
-	$(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib -Z unstable-options $(UNSTABLEOPTS) \
+	$(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \
 		-C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
 	rm $(TMPDIR)/main.dwp
 	rm $(TMPDIR)/$(call BIN,main)
 
-unpacked: unpacked-split unpacked-single unpacked-remapped-split unpacked-remapped-single
+unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate
 
+# - Debuginfo in `.dwo` files
+# - `.o` deleted
+# - `.dwo` present
+# - `.dwp` never created
 unpacked-split:
 	$(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	rm $(TMPDIR)/*.dwo
 	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
-	ls $(TMPDIR)/*.dwo
-	rm -rf $(TMPDIR)/*.dwp $(TMPDIR)/*.dwo
+	rm $(TMPDIR)/$(call BIN,foo)
 
+# - Debuginfo in `.o` files
+# - `.o` present
+# - `.dwo` never created
+# - `.dwp` never created
 unpacked-single:
 	$(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single
+	ls $(TMPDIR)/*.o
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/$(call BIN,foo)
+
+unpacked-lto: packed-lto-split packed-lto-single
+
+# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated
+# - `.o` never created
+# - `.dwo` never created
+# - `.dwp` never created
+unpacked-lto-split:
+	$(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \
+		--crate-type=rlib -Clinker-plugin-lto
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/libbaz.rlib
+
+# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated
+# - `.o` never created
+# - `.dwo` never created
+# - `.dwp` never created
+unpacked-lto-single:
+	$(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \
+		--crate-type=rlib -Clinker-plugin-lto
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
 	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/libbaz.rlib
+
+unpacked-remapped: unpacked-remapped-split unpacked-remapped-single
 
+# - Debuginfo in `.dwo` files
+# - `.o` and binary refer to remapped `.dwo` paths which do not exist
+# - `.o` deleted
+# - `.dwo` present
+# - `.dwp` never created
 unpacked-remapped-split:
 	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \
 		-Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g
 	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	rm $(TMPDIR)/*.dwo
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/$(call BIN,foo)
 
+# - Debuginfo in `.o` files
+# - `.o` and binary refer to remapped `.o` paths which do not exist
+# - `.o` present
+# - `.dwo` never created
+# - `.dwp` never created
 unpacked-remapped-single:
 	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \
 		-Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g
 	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1
+	ls $(TMPDIR)/*.o
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/$(call BIN,foo)
+
+unpacked-crosscrate: packed-crosscrate-split packed-crosscrate-single
+
+# - Debuginfo in `.dwo` files
+# - (bar) `.rlib` file created, contains `.dwo`
+# - (bar) `.o` deleted
+# - (bar) `.dwo` present
+# - (bar) `.dwp` never created
+# - (main) `.o` deleted
+# - (main) `.dwo` present
+# - (main) `.dwp` never created
+unpacked-crosscrate-split:
+	$(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \
+		-Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs
+	ls $(TMPDIR)/*.rlib
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	$(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \
+		-C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	rm $(TMPDIR)/*.dwo
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/$(call BIN,main)
+
+# - Debuginfo in `.o` files
+# - (bar) `.rlib` file created, contains `.o`
+# - (bar) `.o` present
+# - (bar) `.dwo` never created
+# - (bar) `.dwp` never created
+# - (main) `.o` present
+# - (main) `.dwo` never created
+# - (main) `.dwp` never created
+unpacked-crosscrate-single:
+	$(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \
+		-Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs
+	ls $(TMPDIR)/*.rlib
+	ls $(TMPDIR)/*.o
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	$(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \
+		-C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs
+	ls $(TMPDIR)/*.o
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/$(call BIN,main)
 endif
 endif
diff --git a/src/test/run-make-fulldeps/split-debuginfo/baz.rs b/src/test/run-make-fulldeps/split-debuginfo/baz.rs
new file mode 100644
index 00000000000..8b1a393741c
--- /dev/null
+++ b/src/test/run-make-fulldeps/split-debuginfo/baz.rs
@@ -0,0 +1 @@
+// empty
diff --git a/src/test/rustdoc-gui/help-page.goml b/src/test/rustdoc-gui/help-page.goml
index 521e14748af..392f17bfd47 100644
--- a/src/test/rustdoc-gui/help-page.goml
+++ b/src/test/rustdoc-gui/help-page.goml
@@ -3,6 +3,7 @@ goto: "file://" + |DOC_PATH| + "/help.html"
 size: (1000, 1000) // Try desktop size first.
 wait-for: "#help"
 assert-css: ("#help", {"display": "block"})
+assert-css: ("#help dd", {"font-size": "16px"})
 click: "#help-button > a"
 assert-css: ("#help", {"display": "block"})
 compare-elements-property: (".sub", "#help", ["offsetWidth"])
@@ -18,6 +19,7 @@ size: (1000, 1000) // Only supported on desktop.
 assert-false: "#help"
 click: "#help-button > a"
 assert-css: ("#help", {"display": "block"})
+assert-css: ("#help dd", {"font-size": "16px"})
 click: "#help-button > a"
 assert-css: ("#help", {"display": "none"})
 compare-elements-property-false: (".sub", "#help", ["offsetWidth"])
diff --git a/src/test/rustdoc-gui/search-keyboard.goml b/src/test/rustdoc-gui/search-keyboard.goml
new file mode 100644
index 00000000000..be642fc4997
--- /dev/null
+++ b/src/test/rustdoc-gui/search-keyboard.goml
@@ -0,0 +1,28 @@
+// Checks that the search tab results work correctly with function signature syntax
+// First, try a search-by-name
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "Foo")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#titles"
+
+// Now use the keyboard commands to switch to the third result.
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+assert: ".search-results.active > a:focus:nth-of-type(3)"
+
+// Now switch to the second tab, then back to the first one, then arrow back up.
+press-key: "ArrowRight"
+assert: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)"
+press-key: "ArrowLeft"
+assert: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)"
+press-key: "ArrowUp"
+assert: ".search-results.active > a:focus:nth-of-type(2)"
+press-key: "ArrowUp"
+assert: ".search-results.active > a:focus:nth-of-type(1)"
+press-key: "ArrowUp"
+assert: ".search-input:focus"
+press-key: "ArrowDown"
+assert: ".search-results.active > a:focus:nth-of-type(1)"
diff --git a/src/test/rustdoc-gui/type-declation-overflow.goml b/src/test/rustdoc-gui/type-declation-overflow.goml
index dcffe956c21..c014eb52e71 100644
--- a/src/test/rustdoc-gui/type-declation-overflow.goml
+++ b/src/test/rustdoc-gui/type-declation-overflow.goml
@@ -41,3 +41,20 @@ goto: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLon
 store-property: (scrollWidth, ".mobile-topbar h2", "scrollWidth")
 assert-property: (".mobile-topbar h2", {"clientWidth": |scrollWidth|})
 assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"})
+
+// Check wrapping for top main-heading h1 and out-of-band.
+// On desktop, they wrap when too big.
+size: (1100, 800)
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+goto: "file://" + |DOC_PATH| + "/lib2/index.html"
+compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+// make sure there is a gap between them
+compare-elements-position-near-false: (".main-heading h1", ".main-heading .out-of-band", {"x": 550})
+
+// On mobile, they always wrap.
+size: (600, 600)
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+goto: "file://" + |DOC_PATH| + "/lib2/index.html"
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
diff --git a/src/test/rustdoc/intra-doc/issue-104145.rs b/src/test/rustdoc/intra-doc/issue-104145.rs
new file mode 100644
index 00000000000..9ce36740d60
--- /dev/null
+++ b/src/test/rustdoc/intra-doc/issue-104145.rs
@@ -0,0 +1,14 @@
+// Doc links in `Trait`'s methods are resolved because it has a local impl.
+
+// aux-build:issue-103463-aux.rs
+
+extern crate issue_103463_aux;
+use issue_103463_aux::Trait;
+
+pub struct LocalType;
+
+impl Trait for LocalType {
+    fn method() {}
+}
+
+fn main() {}
diff --git a/src/test/rustdoc/issue-102154.rs b/src/test/rustdoc/issue-102154.rs
new file mode 100644
index 00000000000..b36f270806f
--- /dev/null
+++ b/src/test/rustdoc/issue-102154.rs
@@ -0,0 +1,13 @@
+trait A<Y, N> {
+    type B;
+}
+type MaybeBox<T> = <T as A<T, Box<T>>>::B;
+struct P {
+    t: MaybeBox<P>
+}
+impl<Y, N> A<Y, N> for P {
+    type B = N;
+}
+fn main() {
+    let t: MaybeBox<P>;
+}
diff --git a/src/test/rustdoc/multiple-import-levels.rs b/src/test/rustdoc/multiple-import-levels.rs
new file mode 100644
index 00000000000..1daae49cde9
--- /dev/null
+++ b/src/test/rustdoc/multiple-import-levels.rs
@@ -0,0 +1,34 @@
+// The goal of this test is to ensure that the attributes of all imports are taken into
+// account.
+
+#![crate_name = "foo"]
+
+mod a {
+    /// 1
+    pub struct Type;
+}
+
+mod b {
+    /// 2
+    pub use crate::a::Type;
+}
+
+mod c {
+    /// 3
+    pub use crate::b::Type;
+    /// 4
+    pub use crate::b::Type as Woof;
+}
+
+// @has 'foo/struct.Type.html'
+// @has - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]' 'foo 2 1'
+/// foo
+pub use b::Type;
+// @has 'foo/struct.Whatever.html'
+// @has - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]' 'whatever 3 2 1'
+/// whatever
+pub use c::Type as Whatever;
+// @has 'foo/struct.Woof.html'
+// @has - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]' 'a dog 4 2 1'
+/// a dog
+pub use c::Woof;
diff --git a/src/test/ui-fulldeps/uninit_mask.rs b/src/test/ui-fulldeps/uninit_mask.rs
deleted file mode 100644
index 84ce291016a..00000000000
--- a/src/test/ui-fulldeps/uninit_mask.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-// run-pass
-// ignore-cross-compile
-// ignore-stage1
-
-#![feature(rustc_private)]
-
-extern crate rustc_middle;
-extern crate rustc_target;
-
-use rustc_middle::mir::interpret::InitMask;
-use rustc_target::abi::Size;
-
-fn main() {
-    let mut mask = InitMask::new(Size::from_bytes(500), false);
-    assert!(!mask.get(Size::from_bytes(499)));
-    mask.set(Size::from_bytes(499), true);
-    assert!(mask.get(Size::from_bytes(499)));
-    mask.set_range_inbounds(Size::from_bytes(100), Size::from_bytes(256), true);
-    for i in 0..100 {
-        assert!(!mask.get(Size::from_bytes(i)));
-    }
-    for i in 100..256 {
-        assert!(mask.get(Size::from_bytes(i)));
-    }
-    for i in 256..499 {
-        assert!(!mask.get(Size::from_bytes(i)));
-    }
-}
diff --git a/src/test/ui/array-slice-vec/vec-macro-with-comma-only.stderr b/src/test/ui/array-slice-vec/vec-macro-with-comma-only.stderr
index abbee347c00..ec4a001f4d0 100644
--- a/src/test/ui/array-slice-vec/vec-macro-with-comma-only.stderr
+++ b/src/test/ui/array-slice-vec/vec-macro-with-comma-only.stderr
@@ -3,6 +3,8 @@ error: no rules expected the token `,`
    |
 LL |     vec![,];
    |          ^ no rules expected this token in macro call
+   |
+   = note: while trying to match end of macro
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/associated-consts/defaults-cyclic-fail.stderr b/src/test/ui/associated-consts/defaults-cyclic-fail.stderr
index c4cd9c2a49f..a1483911b29 100644
--- a/src/test/ui/associated-consts/defaults-cyclic-fail.stderr
+++ b/src/test/ui/associated-consts/defaults-cyclic-fail.stderr
@@ -1,14 +1,14 @@
 error[E0391]: cycle detected when const-evaluating + checking `Tr::A`
-  --> $DIR/defaults-cyclic-fail.rs:5:5
+  --> $DIR/defaults-cyclic-fail.rs:5:19
    |
 LL |     const A: u8 = Self::B;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |                   ^^^^^^^
    |
 note: ...which requires const-evaluating + checking `Tr::B`...
-  --> $DIR/defaults-cyclic-fail.rs:8:5
+  --> $DIR/defaults-cyclic-fail.rs:8:19
    |
 LL |     const B: u8 = Self::A;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |                   ^^^^^^^
    = note: ...which again requires const-evaluating + checking `Tr::A`, completing the cycle
 note: cycle used when const-evaluating + checking `main::promoted[1]`
   --> $DIR/defaults-cyclic-fail.rs:16:16
diff --git a/src/test/ui/associated-consts/defaults-not-assumed-fail.rs b/src/test/ui/associated-consts/defaults-not-assumed-fail.rs
index 6762d7583fb..495dfb338ae 100644
--- a/src/test/ui/associated-consts/defaults-not-assumed-fail.rs
+++ b/src/test/ui/associated-consts/defaults-not-assumed-fail.rs
@@ -31,8 +31,7 @@ impl Tr for u32 {
 fn main() {
     assert_eq!(<() as Tr>::A, 255);
     assert_eq!(<() as Tr>::B, 0);    // causes the error above
-    //~^ ERROR evaluation of constant value failed
-    //~| ERROR erroneous constant used
+    //~^ constant
 
     assert_eq!(<u8 as Tr>::A, 254);
     assert_eq!(<u8 as Tr>::B, 255);
diff --git a/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr b/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr
index aa130f438a8..fb7159e40c9 100644
--- a/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr
+++ b/src/test/ui/associated-consts/defaults-not-assumed-fail.stderr
@@ -4,20 +4,36 @@ error[E0080]: evaluation of `<() as Tr>::B` failed
 LL |     const B: u8 = Self::A + 1;
    |                   ^^^^^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/defaults-not-assumed-fail.rs:33:16
    |
 LL |     assert_eq!(<() as Tr>::B, 0);    // causes the error above
-   |                ^^^^^^^^^^^^^ referenced constant has errors
+   |                ^^^^^^^^^^^^^
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/defaults-not-assumed-fail.rs:33:5
    |
 LL |     assert_eq!(<() as Tr>::B, 0);    // causes the error above
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 3 previous errors
+note: erroneous constant used
+  --> $DIR/defaults-not-assumed-fail.rs:33:5
+   |
+LL |     assert_eq!(<() as Tr>::B, 0);    // causes the error above
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: erroneous constant used
+  --> $DIR/defaults-not-assumed-fail.rs:33:5
+   |
+LL |     assert_eq!(<() as Tr>::B, 0);    // causes the error above
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
index c8c57bccb50..be578176151 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-impl.stderr
@@ -10,10 +10,10 @@ note: ...which requires const-evaluating + checking `IMPL_REF_BAR`...
 LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR;
    | ^^^^^^^^^^^^^^^^^^^^^^^
 note: ...which requires const-evaluating + checking `IMPL_REF_BAR`...
-  --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:1
+  --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:7:27
    |
 LL | const IMPL_REF_BAR: u32 = GlobalImplRef::BAR;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                           ^^^^^^^^^^^^^^^^^^
 note: ...which requires const-evaluating + checking `<impl at $DIR/issue-24949-assoc-const-static-recursion-impl.rs:11:1: 11:19>::BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-impl.rs:12:5
    |
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
index 76ed8d4a6e8..8347b260b56 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait-default.stderr
@@ -10,10 +10,10 @@ note: ...which requires const-evaluating + checking `DEFAULT_REF_BAR`...
 LL | const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: ...which requires const-evaluating + checking `DEFAULT_REF_BAR`...
-  --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:1
+  --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:11:30
    |
 LL | const DEFAULT_REF_BAR: u32 = <GlobalDefaultRef>::BAR;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^
 note: ...which requires const-evaluating + checking `FooDefault::BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-trait-default.rs:8:5
    |
diff --git a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
index 6a98f08f3d3..3955a3120c4 100644
--- a/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
+++ b/src/test/ui/associated-consts/issue-24949-assoc-const-static-recursion-trait.stderr
@@ -10,10 +10,10 @@ note: ...which requires const-evaluating + checking `TRAIT_REF_BAR`...
 LL | const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR;
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 note: ...which requires const-evaluating + checking `TRAIT_REF_BAR`...
-  --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:1
+  --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:7:28
    |
 LL | const TRAIT_REF_BAR: u32 = <GlobalTraitRef>::BAR;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                            ^^^^^^^^^^^^^^^^^^^^^
 note: ...which requires const-evaluating + checking `<impl at $DIR/issue-24949-assoc-const-static-recursion-trait.rs:11:1: 11:28>::BAR`...
   --> $DIR/issue-24949-assoc-const-static-recursion-trait.rs:12:5
    |
diff --git a/src/test/ui/associated-inherent-types/assoc-inherent-private.rs b/src/test/ui/associated-inherent-types/assoc-inherent-private.rs
new file mode 100644
index 00000000000..53158195443
--- /dev/null
+++ b/src/test/ui/associated-inherent-types/assoc-inherent-private.rs
@@ -0,0 +1,23 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+mod m {
+    pub struct T;
+    impl T {
+        type P = ();
+    }
+}
+type U = m::T::P; //~ ERROR associated type `P` is private
+
+mod n {
+    pub mod n {
+        pub struct T;
+        impl T {
+            pub(super) type P = bool;
+        }
+    }
+    type U = n::T::P;
+}
+type V = n::n::T::P; //~ ERROR associated type `P` is private
+
+fn main() {}
diff --git a/src/test/ui/associated-inherent-types/assoc-inherent-private.stderr b/src/test/ui/associated-inherent-types/assoc-inherent-private.stderr
new file mode 100644
index 00000000000..d67b45dae3f
--- /dev/null
+++ b/src/test/ui/associated-inherent-types/assoc-inherent-private.stderr
@@ -0,0 +1,21 @@
+error[E0624]: associated type `P` is private
+  --> $DIR/assoc-inherent-private.rs:10:10
+   |
+LL |         type P = ();
+   |         ------ associated type defined here
+...
+LL | type U = m::T::P;
+   |          ^^^^^^^ private associated type
+
+error[E0624]: associated type `P` is private
+  --> $DIR/assoc-inherent-private.rs:21:10
+   |
+LL |             pub(super) type P = bool;
+   |             ----------------- associated type defined here
+...
+LL | type V = n::n::T::P;
+   |          ^^^^^^^^^^ private associated type
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0624`.
diff --git a/src/test/ui/associated-inherent-types/assoc-inherent-unstable.rs b/src/test/ui/associated-inherent-types/assoc-inherent-unstable.rs
new file mode 100644
index 00000000000..34b4e47bf46
--- /dev/null
+++ b/src/test/ui/associated-inherent-types/assoc-inherent-unstable.rs
@@ -0,0 +1,6 @@
+// aux-crate:aux=assoc-inherent-unstable.rs
+// edition: 2021
+
+type Data = aux::Owner::Data; //~ ERROR use of unstable library feature 'data'
+
+fn main() {}
diff --git a/src/test/ui/associated-inherent-types/assoc-inherent-unstable.stderr b/src/test/ui/associated-inherent-types/assoc-inherent-unstable.stderr
new file mode 100644
index 00000000000..c0be8bfd79b
--- /dev/null
+++ b/src/test/ui/associated-inherent-types/assoc-inherent-unstable.stderr
@@ -0,0 +1,11 @@
+error[E0658]: use of unstable library feature 'data'
+  --> $DIR/assoc-inherent-unstable.rs:4:13
+   |
+LL | type Data = aux::Owner::Data;
+   |             ^^^^^^^^^^^^^^^^
+   |
+   = help: add `#![feature(data)]` 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/src/test/ui/associated-inherent-types/auxiliary/assoc-inherent-unstable.rs b/src/test/ui/associated-inherent-types/auxiliary/assoc-inherent-unstable.rs
new file mode 100644
index 00000000000..6b71ffc97b5
--- /dev/null
+++ b/src/test/ui/associated-inherent-types/auxiliary/assoc-inherent-unstable.rs
@@ -0,0 +1,11 @@
+#![feature(staged_api)]
+#![feature(inherent_associated_types)]
+#![stable(feature = "main", since = "1.0.0")]
+
+#[stable(feature = "main", since = "1.0.0")]
+pub struct Owner;
+
+impl Owner {
+    #[unstable(feature = "data", issue = "none")]
+    pub type Data = ();
+}
diff --git a/src/test/ui/async-await/track-caller/panic-track-caller.rs b/src/test/ui/async-await/track-caller/panic-track-caller.rs
new file mode 100644
index 00000000000..b113c56412f
--- /dev/null
+++ b/src/test/ui/async-await/track-caller/panic-track-caller.rs
@@ -0,0 +1,76 @@
+// run-pass
+// edition:2021
+// needs-unwind
+#![feature(closure_track_caller)]
+
+use std::future::Future;
+use std::panic;
+use std::sync::{Arc, Mutex};
+use std::task::{Context, Poll, Wake};
+use std::thread::{self, Thread};
+
+/// A waker that wakes up the current thread when called.
+struct ThreadWaker(Thread);
+
+impl Wake for ThreadWaker {
+    fn wake(self: Arc<Self>) {
+        self.0.unpark();
+    }
+}
+
+/// Run a future to completion on the current thread.
+fn block_on<T>(fut: impl Future<Output = T>) -> T {
+    // Pin the future so it can be polled.
+    let mut fut = Box::pin(fut);
+
+    // Create a new context to be passed to the future.
+    let t = thread::current();
+    let waker = Arc::new(ThreadWaker(t)).into();
+    let mut cx = Context::from_waker(&waker);
+
+    // Run the future to completion.
+    loop {
+        match fut.as_mut().poll(&mut cx) {
+            Poll::Ready(res) => return res,
+            Poll::Pending => thread::park(),
+        }
+    }
+}
+
+async fn bar() {
+    panic!()
+}
+
+async fn foo() {
+    bar().await
+}
+
+#[track_caller]
+async fn bar_track_caller() {
+    panic!()
+}
+
+async fn foo_track_caller() {
+    bar_track_caller().await
+}
+
+fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 {
+    let loc = Arc::new(Mutex::new(None));
+
+    let hook = panic::take_hook();
+    {
+        let loc = loc.clone();
+        panic::set_hook(Box::new(move |info| {
+            *loc.lock().unwrap() = info.location().map(|loc| loc.line())
+        }));
+    }
+    panic::catch_unwind(f).unwrap_err();
+    panic::set_hook(hook);
+    let x = loc.lock().unwrap().unwrap();
+    x
+}
+
+fn main() {
+    assert_eq!(panicked_at(|| block_on(foo())), 41);
+    assert_eq!(panicked_at(|| block_on(foo_track_caller())), 54);
+}
diff --git a/src/test/ui/borrowck/async-reference-generality.rs b/src/test/ui/borrowck/async-reference-generality.rs
new file mode 100644
index 00000000000..487d1ac81bf
--- /dev/null
+++ b/src/test/ui/borrowck/async-reference-generality.rs
@@ -0,0 +1,35 @@
+// check-fail
+// known-bug: #99492
+// edition: 2021
+
+use std::marker::PhantomData;
+
+pub struct Struct<I, T>(PhantomData<fn() -> <Self as It>::Item>)
+where
+    Self: It;
+
+impl<I> It for Struct<I, I::Item>
+where
+    I: It,
+{
+    type Item = ();
+}
+
+pub trait It {
+    type Item;
+}
+
+fn f() -> impl Send {
+    async {
+        let _x = Struct::<Empty<&'static ()>, _>(PhantomData);
+        async {}.await;
+    }
+}
+
+pub struct Empty<T>(PhantomData<fn() -> T>);
+
+impl<T> It for Empty<T> {
+    type Item = T;
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/async-reference-generality.stderr b/src/test/ui/borrowck/async-reference-generality.stderr
new file mode 100644
index 00000000000..af720ad2931
--- /dev/null
+++ b/src/test/ui/borrowck/async-reference-generality.stderr
@@ -0,0 +1,27 @@
+error[E0308]: mismatched types
+  --> $DIR/async-reference-generality.rs:23:5
+   |
+LL | /     async {
+LL | |         let _x = Struct::<Empty<&'static ()>, _>(PhantomData);
+LL | |         async {}.await;
+LL | |     }
+   | |_____^ one type is more general than the other
+   |
+   = note: expected reference `&()`
+              found reference `&()`
+
+error[E0308]: mismatched types
+  --> $DIR/async-reference-generality.rs:23:5
+   |
+LL | /     async {
+LL | |         let _x = Struct::<Empty<&'static ()>, _>(PhantomData);
+LL | |         async {}.await;
+LL | |     }
+   | |_____^ one type is more general than the other
+   |
+   = note: expected reference `&()`
+              found reference `&()`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/borrowck/issue-81899.rs b/src/test/ui/borrowck/issue-81899.rs
index 24b20b6507b..1f1af5c7e05 100644
--- a/src/test/ui/borrowck/issue-81899.rs
+++ b/src/test/ui/borrowck/issue-81899.rs
@@ -2,13 +2,14 @@
 // The `panic!()` below is important to trigger the fixed ICE.
 
 const _CONST: &[u8] = &f(&[], |_| {});
-//~^ ERROR constant
+//~^ constant
 
 const fn f<F>(_: &[u8], _: F) -> &[u8]
 where
     F: FnMut(&u8),
 {
-    panic!() //~ ERROR: evaluation of constant value failed
+    panic!() //~ ERROR evaluation of constant value failed
+    //~^ panic
 }
 
 fn main() {}
diff --git a/src/test/ui/borrowck/issue-81899.stderr b/src/test/ui/borrowck/issue-81899.stderr
index 12e80b9df82..a4d5f212188 100644
--- a/src/test/ui/borrowck/issue-81899.stderr
+++ b/src/test/ui/borrowck/issue-81899.stderr
@@ -12,12 +12,12 @@ LL |     panic!()
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-81899.rs:4:23
    |
 LL | const _CONST: &[u8] = &f(&[], |_| {});
-   |                       ^^^^^^^^^^^^^^^ referenced constant has errors
+   |                       ^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/borrowck/issue-88434-minimal-example.rs b/src/test/ui/borrowck/issue-88434-minimal-example.rs
index 983a0231052..b75abcb731e 100644
--- a/src/test/ui/borrowck/issue-88434-minimal-example.rs
+++ b/src/test/ui/borrowck/issue-88434-minimal-example.rs
@@ -1,13 +1,14 @@
 // Regression test related to issue 88434
 
 const _CONST: &() = &f(&|_| {});
-//~^ ERROR constant
+//~^ constant
 
 const fn f<F>(_: &F)
 where
     F: FnMut(&u8),
 {
     panic!() //~ ERROR evaluation of constant value failed
+    //~^ panic
 }
 
 fn main() { }
diff --git a/src/test/ui/borrowck/issue-88434-minimal-example.stderr b/src/test/ui/borrowck/issue-88434-minimal-example.stderr
index dc87c4c2b07..b95ddc49c99 100644
--- a/src/test/ui/borrowck/issue-88434-minimal-example.stderr
+++ b/src/test/ui/borrowck/issue-88434-minimal-example.stderr
@@ -12,12 +12,12 @@ LL |     panic!()
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-88434-minimal-example.rs:3:21
    |
 LL | const _CONST: &() = &f(&|_| {});
-   |                     ^^^^^^^^^^^ referenced constant has errors
+   |                     ^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.rs b/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.rs
index a99c5b76a4e..f9134e669dc 100644
--- a/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.rs
+++ b/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.rs
@@ -1,13 +1,14 @@
 // Regression test for issue 88434
 
 const _CONST: &[u8] = &f(&[], |_| {});
-//~^ ERROR constant
+//~^ constant
 
 const fn f<F>(_: &[u8], _: F) -> &[u8]
 where
     F: FnMut(&u8),
 {
     panic!() //~ ERROR evaluation of constant value failed
+    //~^ panic
 }
 
 fn main() { }
diff --git a/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.stderr b/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.stderr
index 4b4a25d7be1..604a6577639 100644
--- a/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.stderr
+++ b/src/test/ui/borrowck/issue-88434-removal-index-should-be-less.stderr
@@ -12,12 +12,12 @@ LL |     panic!()
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-88434-removal-index-should-be-less.rs:3:23
    |
 LL | const _CONST: &[u8] = &f(&[], |_| {});
-   |                       ^^^^^^^^^^^^^^^ referenced constant has errors
+   |                       ^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/chalkify/closure.rs b/src/test/ui/chalkify/closure.rs
index 408e8802d86..568e2e30c41 100644
--- a/src/test/ui/chalkify/closure.rs
+++ b/src/test/ui/chalkify/closure.rs
@@ -1,5 +1,3 @@
-// known-bug: unknown
-// FIXME(chalk): Chalk needs support for the Tuple trait
 // compile-flags: -Z chalk
 
 fn main() -> () {
@@ -26,7 +24,7 @@ fn main() -> () {
     let mut c = b;
 
     c();
-    b(); // FIXME: reenable when this is fixed ~ ERROR
+    b(); //~ ERROR
 
     // FIXME(chalk): this doesn't quite work
     /*
diff --git a/src/test/ui/chalkify/closure.stderr b/src/test/ui/chalkify/closure.stderr
index bcee0cab96a..a33c0ba0d37 100644
--- a/src/test/ui/chalkify/closure.stderr
+++ b/src/test/ui/chalkify/closure.stderr
@@ -1,80 +1,22 @@
-error[E0277]: `()` is not a tuple
-  --> $DIR/closure.rs:7:5
-   |
-LL |     t();
-   |     ^^^ the trait `Tuple` is not implemented for `()`
-   |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() -> () where (): Tuple {
-   |                 +++++++++++++++
-
-error[E0277]: `()` is not a tuple
-  --> $DIR/closure.rs:13:5
-   |
-LL |     b();
-   |     ^^^ the trait `Tuple` is not implemented for `()`
-   |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() -> () where (): Tuple {
-   |                 +++++++++++++++
-
-error[E0277]: `()` is not a tuple
-  --> $DIR/closure.rs:17:5
-   |
-LL |     c();
-   |     ^^^ the trait `Tuple` is not implemented for `()`
-   |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() -> () where (): Tuple {
-   |                 +++++++++++++++
-
-error[E0277]: `()` is not a tuple
-  --> $DIR/closure.rs:18:5
+error[E0382]: borrow of moved value: `b`
+  --> $DIR/closure.rs:27:5
    |
+LL |     let mut c = b;
+   |                 - value moved here
+...
 LL |     b();
-   |     ^^^ the trait `Tuple` is not implemented for `()`
-   |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() -> () where (): Tuple {
-   |                 +++++++++++++++
-
-error[E0277]: `()` is not a tuple
-  --> $DIR/closure.rs:24:5
-   |
-LL |     b();
-   |     ^^^ the trait `Tuple` is not implemented for `()`
-   |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() -> () where (): Tuple {
-   |                 +++++++++++++++
-
-error[E0277]: `()` is not a tuple
-  --> $DIR/closure.rs:28:5
-   |
-LL |     c();
-   |     ^^^ the trait `Tuple` is not implemented for `()`
-   |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() -> () where (): Tuple {
-   |                 +++++++++++++++
-
-error[E0277]: `()` is not a tuple
-  --> $DIR/closure.rs:29:5
+   |     ^ value borrowed here after move
    |
-LL |     b(); // FIXME: reenable when this is fixed ~ ERROR
-   |     ^^^ the trait `Tuple` is not implemented for `()`
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `a` out of its environment
+  --> $DIR/closure.rs:20:9
    |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
+LL |         a = 1;
+   |         ^
+help: consider mutably borrowing `b`
    |
-LL | fn main() -> () where (): Tuple {
-   |                 +++++++++++++++
+LL |     let mut c = &mut b;
+   |                 ++++
 
-error: aborting due to 7 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/chalkify/trait-objects.rs b/src/test/ui/chalkify/trait-objects.rs
index 30929e943bd..d56abc42bf5 100644
--- a/src/test/ui/chalkify/trait-objects.rs
+++ b/src/test/ui/chalkify/trait-objects.rs
@@ -1,5 +1,4 @@
-// known-bug: unknown
-// FIXME(chalk): Chalk needs support for the Tuple trait
+// check-pass
 // compile-flags: -Z chalk
 
 use std::fmt::Display;
diff --git a/src/test/ui/chalkify/trait-objects.stderr b/src/test/ui/chalkify/trait-objects.stderr
deleted file mode 100644
index 422d39742eb..00000000000
--- a/src/test/ui/chalkify/trait-objects.stderr
+++ /dev/null
@@ -1,32 +0,0 @@
-error: the type `&dyn Fn(i32) -> _` is not well-formed (chalk)
-  --> $DIR/trait-objects.rs:11:12
-   |
-LL |     let f: &dyn Fn(i32) -> _ = &|x| x + x;
-   |            ^^^^^^^^^^^^^^^^^
-
-error[E0277]: `(i32,)` is not a tuple
-  --> $DIR/trait-objects.rs:12:5
-   |
-LL |     f(2);
-   |     ^^^^ the trait `Tuple` is not implemented for `(i32,)`
-   |
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() where (i32,): Tuple {
-   |           +++++++++++++++++++
-
-error[E0277]: expected a `Fn<(i32,)>` closure, found `dyn Fn(i32) -> i32`
-  --> $DIR/trait-objects.rs:12:5
-   |
-LL |     f(2);
-   |     ^^^^ expected an `Fn<(i32,)>` closure, found `dyn Fn(i32) -> i32`
-   |
-   = help: the trait `Fn<(i32,)>` is not implemented for `dyn Fn(i32) -> i32`
-help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
-   |
-LL | fn main() where dyn Fn(i32) -> i32: Fn<(i32,)> {
-   |           ++++++++++++++++++++++++++++++++++++
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/codemap_tests/unicode_2.stderr b/src/test/ui/codemap_tests/unicode_2.stderr
index a776a4a1e7e..19aae1d3c95 100644
--- a/src/test/ui/codemap_tests/unicode_2.stderr
+++ b/src/test/ui/codemap_tests/unicode_2.stderr
@@ -1,3 +1,9 @@
+error[E0425]: cannot find value `a̐é` in this scope
+  --> $DIR/unicode_2.rs:4:13
+   |
+LL |     let _ = a̐é;
+   |             ^^ not found in this scope
+
 error: invalid width `7` for integer literal
   --> $DIR/unicode_2.rs:2:25
    |
@@ -14,12 +20,6 @@ LL |     let _ = ("아あ", 1i42);
    |
    = help: valid widths are 8, 16, 32, 64 and 128
 
-error[E0425]: cannot find value `a̐é` in this scope
-  --> $DIR/unicode_2.rs:4:13
-   |
-LL |     let _ = a̐é;
-   |             ^^ not found in this scope
-
 error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0425`.
diff --git a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr
index c25c4369292..4d7872598b1 100644
--- a/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr
+++ b/src/test/ui/coherence/coherence-blanket-conflicts-with-specific-cross-crate.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `go_trait::GoMut` for type `MyThingy`
+error[E0119]: conflicting implementations of trait `GoMut` for type `MyThingy`
   --> $DIR/coherence-blanket-conflicts-with-specific-cross-crate.rs:15:1
    |
 LL | impl GoMut for MyThingy {
diff --git a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr
index 1110197734f..2463f38a922 100644
--- a/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr
+++ b/src/test/ui/coherence/coherence-conflicting-negative-trait-impl.stderr
@@ -1,4 +1,4 @@
-error[E0751]: found both positive and negative implementation of trait `std::marker::Send` for type `TestType<_>`:
+error[E0751]: found both positive and negative implementation of trait `Send` for type `TestType<_>`:
   --> $DIR/coherence-conflicting-negative-trait-impl.rs:11:1
    |
 LL | unsafe impl<T: MyTrait + 'static> Send for TestType<T> {}
@@ -7,7 +7,7 @@ LL |
 LL | impl<T: MyTrait> !Send for TestType<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here
 
-error[E0119]: conflicting implementations of trait `std::marker::Send` for type `TestType<_>`
+error[E0119]: conflicting implementations of trait `Send` for type `TestType<_>`
   --> $DIR/coherence-conflicting-negative-trait-impl.rs:13:1
    |
 LL | unsafe impl<T: MyTrait + 'static> Send for TestType<T> {}
diff --git a/src/test/ui/coherence/coherence-impls-copy.stderr b/src/test/ui/coherence/coherence-impls-copy.stderr
index 86356af2564..d40ffc48a29 100644
--- a/src/test/ui/coherence/coherence-impls-copy.stderr
+++ b/src/test/ui/coherence/coherence-impls-copy.stderr
@@ -9,7 +9,7 @@ LL | impl Copy for i32 {}
    |
    = note: define and implement a trait or new type instead
 
-error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync`
+error[E0119]: conflicting implementations of trait `Copy` for type `&NotSync`
   --> $DIR/coherence-impls-copy.rs:28:1
    |
 LL | impl Copy for &'static NotSync {}
diff --git a/src/test/ui/coherence/coherence-overlap-issue-23516.stderr b/src/test/ui/coherence/coherence-overlap-issue-23516.stderr
index 85eb189e10e..cd398426704 100644
--- a/src/test/ui/coherence/coherence-overlap-issue-23516.stderr
+++ b/src/test/ui/coherence/coherence-overlap-issue-23516.stderr
@@ -1,10 +1,10 @@
-error[E0119]: conflicting implementations of trait `Sweet` for type `std::boxed::Box<_>`
+error[E0119]: conflicting implementations of trait `Sweet` for type `Box<_>`
   --> $DIR/coherence-overlap-issue-23516.rs:8:1
    |
 LL | impl<T:Sugar> Sweet for T { }
    | ------------------------- first implementation here
 LL | impl<U:Sugar> Sweet for Box<U> { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::boxed::Box<_>`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
    |
    = note: downstream crates may implement trait `Sugar` for type `std::boxed::Box<_>`
 
diff --git a/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr b/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr
index 6492747bb26..94d242eaac4 100644
--- a/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr
+++ b/src/test/ui/coherence/coherence-projection-conflict-ty-param.stderr
@@ -1,11 +1,11 @@
-error[E0119]: conflicting implementations of trait `Foo<_>` for type `std::option::Option<_>`
+error[E0119]: conflicting implementations of trait `Foo<_>` for type `Option<_>`
   --> $DIR/coherence-projection-conflict-ty-param.rs:10:1
    |
 LL | impl <P, T: Foo<P>> Foo<P> for Option<T> {}
    | ---------------------------------------- first implementation here
 LL |
 LL | impl<T, U> Foo<T> for Option<U> { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::option::Option<_>`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Option<_>`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/coherence/coherence-wasm-bindgen.stderr b/src/test/ui/coherence/coherence-wasm-bindgen.stderr
index cfcc21240e4..89615f0fbc6 100644
--- a/src/test/ui/coherence/coherence-wasm-bindgen.stderr
+++ b/src/test/ui/coherence/coherence-wasm-bindgen.stderr
@@ -1,11 +1,11 @@
-error: conflicting implementations of trait `IntoWasmAbi` for type `&dyn std::ops::Fn(&_) -> _`
+error: conflicting implementations of trait `IntoWasmAbi` for type `&dyn Fn(&_) -> _`
   --> $DIR/coherence-wasm-bindgen.rs:28:1
    |
 LL | impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn Fn(A) -> R + 'b)
    | ------------------------------------------------------------ first implementation here
 ...
 LL | impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn for<'x> Fn(&'x A) -> R + 'b)
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&dyn std::ops::Fn(&_) -> _`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&dyn Fn(&_) -> _`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
diff --git a/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr b/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr
index db730650185..93486fa5f36 100644
--- a/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr
+++ b/src/test/ui/coherence/coherence_copy_like_err_fundamental_struct_tuple.stderr
@@ -1,11 +1,11 @@
-error[E0119]: conflicting implementations of trait `MyTrait` for type `lib::MyFundamentalStruct<(MyType,)>`
+error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFundamentalStruct<(MyType,)>`
   --> $DIR/coherence_copy_like_err_fundamental_struct_tuple.rs:16:1
    |
 LL | impl<T: lib::MyCopy> MyTrait for T { }
    | ---------------------------------- first implementation here
 ...
 LL | impl MyTrait for lib::MyFundamentalStruct<(MyType,)> { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `lib::MyFundamentalStruct<(MyType,)>`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFundamentalStruct<(MyType,)>`
    |
    = note: upstream crates may add a new impl of trait `lib::MyCopy` for type `lib::MyFundamentalStruct<(MyType,)>` in future versions
 
diff --git a/src/test/ui/coherence/coherence_copy_like_err_struct.stderr b/src/test/ui/coherence/coherence_copy_like_err_struct.stderr
index 3bc3dffda5d..7432733b932 100644
--- a/src/test/ui/coherence/coherence_copy_like_err_struct.stderr
+++ b/src/test/ui/coherence/coherence_copy_like_err_struct.stderr
@@ -1,11 +1,11 @@
-error[E0119]: conflicting implementations of trait `MyTrait` for type `lib::MyStruct<MyType>`
+error[E0119]: conflicting implementations of trait `MyTrait` for type `MyStruct<MyType>`
   --> $DIR/coherence_copy_like_err_struct.rs:19:1
    |
 LL | impl<T: lib::MyCopy> MyTrait for T { }
    | ---------------------------------- first implementation here
 ...
 LL | impl MyTrait for lib::MyStruct<MyType> { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `lib::MyStruct<MyType>`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyStruct<MyType>`
    |
    = note: upstream crates may add a new impl of trait `lib::MyCopy` for type `lib::MyStruct<MyType>` in future versions
 
diff --git a/src/test/ui/coherence/inter-crate-ambiguity-causes-notes.stderr b/src/test/ui/coherence/inter-crate-ambiguity-causes-notes.stderr
index 038a0199a8f..4ddd712b27c 100644
--- a/src/test/ui/coherence/inter-crate-ambiguity-causes-notes.stderr
+++ b/src/test/ui/coherence/inter-crate-ambiguity-causes-notes.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `std::convert::From<()>` for type `S`
+error[E0119]: conflicting implementations of trait `From<()>` for type `S`
   --> $DIR/inter-crate-ambiguity-causes-notes.rs:9:1
    |
 LL | impl From<()> for S {
diff --git a/src/test/ui/const-generics/min_const_generics/macro-fail.stderr b/src/test/ui/const-generics/min_const_generics/macro-fail.stderr
index 2b75c197748..9f73b91aabe 100644
--- a/src/test/ui/const-generics/min_const_generics/macro-fail.stderr
+++ b/src/test/ui/const-generics/min_const_generics/macro-fail.stderr
@@ -53,6 +53,12 @@ LL |     macro_rules! gimme_a_const {
 ...
 LL |   let _fail = Example::<gimme_a_const!()>;
    |                         ^^^^^^^^^^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$rusty:ident`
+  --> $DIR/macro-fail.rs:28:8
+   |
+LL |       ($rusty: ident) => {{ let $rusty = 3; *&$rusty }}
+   |        ^^^^^^^^^^^^^
 
 error[E0747]: type provided when a constant was expected
   --> $DIR/macro-fail.rs:14:33
diff --git a/src/test/ui/consts/const-err-late.rs b/src/test/ui/consts/const-err-late.rs
index a20ae702586..d2476e49346 100644
--- a/src/test/ui/consts/const-err-late.rs
+++ b/src/test/ui/consts/const-err-late.rs
@@ -16,7 +16,5 @@ impl<T> S<T> {
 }
 
 fn main() {
-    black_box((S::<i32>::FOO, S::<u32>::FOO));
-    //~^ ERROR erroneous constant
-    //~| ERROR erroneous constant
+    black_box((S::<i32>::FOO, S::<u32>::FOO)); //~ constant
 }
diff --git a/src/test/ui/consts/const-err-late.stderr b/src/test/ui/consts/const-err-late.stderr
index 3a8b103175b..c5c668189b9 100644
--- a/src/test/ui/consts/const-err-late.stderr
+++ b/src/test/ui/consts/const-err-late.stderr
@@ -4,11 +4,17 @@ error[E0080]: evaluation of `S::<i32>::FOO` failed
 LL |     const FOO: u8 = [5u8][1];
    |                     ^^^^^^^^ index out of bounds: the length is 1 but the index is 1
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/const-err-late.rs:19:16
    |
 LL |     black_box((S::<i32>::FOO, S::<u32>::FOO));
-   |                ^^^^^^^^^^^^^ referenced constant has errors
+   |                ^^^^^^^^^^^^^
+
+note: erroneous constant used
+  --> $DIR/const-err-late.rs:19:16
+   |
+LL |     black_box((S::<i32>::FOO, S::<u32>::FOO));
+   |                ^^^^^^^^^^^^^
 
 error[E0080]: evaluation of `S::<u32>::FOO` failed
   --> $DIR/const-err-late.rs:13:21
@@ -16,12 +22,30 @@ error[E0080]: evaluation of `S::<u32>::FOO` failed
 LL |     const FOO: u8 = [5u8][1];
    |                     ^^^^^^^^ index out of bounds: the length is 1 but the index is 1
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
+  --> $DIR/const-err-late.rs:19:31
+   |
+LL |     black_box((S::<i32>::FOO, S::<u32>::FOO));
+   |                               ^^^^^^^^^^^^^
+
+note: erroneous constant used
+  --> $DIR/const-err-late.rs:19:31
+   |
+LL |     black_box((S::<i32>::FOO, S::<u32>::FOO));
+   |                               ^^^^^^^^^^^^^
+
+note: erroneous constant used
+  --> $DIR/const-err-late.rs:19:16
+   |
+LL |     black_box((S::<i32>::FOO, S::<u32>::FOO));
+   |                ^^^^^^^^^^^^^
+
+note: erroneous constant used
   --> $DIR/const-err-late.rs:19:31
    |
 LL |     black_box((S::<i32>::FOO, S::<u32>::FOO));
-   |                               ^^^^^^^^^^^^^ referenced constant has errors
+   |                               ^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-err-multi.rs b/src/test/ui/consts/const-err-multi.rs
index fb26e8aac10..b265bc4c4d8 100644
--- a/src/test/ui/consts/const-err-multi.rs
+++ b/src/test/ui/consts/const-err-multi.rs
@@ -1,11 +1,11 @@
 pub const A: i8 = -i8::MIN;
 //~^ ERROR constant
 pub const B: i8 = A;
-//~^ ERROR constant
+//~^ constant
 pub const C: u8 = A as u8;
-//~^ ERROR constant
+//~^ constant
 pub const D: i8 = 50 - A;
-//~^ ERROR constant
+//~^ constant
 
 fn main() {
     let _ = (A, B, C, D);
diff --git a/src/test/ui/consts/const-err-multi.stderr b/src/test/ui/consts/const-err-multi.stderr
index fca9e227068..28af8e5eb09 100644
--- a/src/test/ui/consts/const-err-multi.stderr
+++ b/src/test/ui/consts/const-err-multi.stderr
@@ -4,24 +4,24 @@ error[E0080]: evaluation of constant value failed
 LL | pub const A: i8 = -i8::MIN;
    |                   ^^^^^^^^ attempt to negate `i8::MIN`, which would overflow
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-err-multi.rs:3:19
    |
 LL | pub const B: i8 = A;
-   |                   ^ referenced constant has errors
+   |                   ^
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-err-multi.rs:5:19
    |
 LL | pub const C: u8 = A as u8;
-   |                   ^ referenced constant has errors
+   |                   ^
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-err-multi.rs:7:24
    |
 LL | pub const D: i8 = 50 - A;
-   |                        ^ referenced constant has errors
+   |                        ^
 
-error: aborting due to 4 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/erroneous-const.rs b/src/test/ui/consts/const-eval/erroneous-const.rs
index cf11531ba62..e0fd057a241 100644
--- a/src/test/ui/consts/const-eval/erroneous-const.rs
+++ b/src/test/ui/consts/const-eval/erroneous-const.rs
@@ -10,7 +10,7 @@ const fn no_codegen<T>() {
     if false {
         // This bad constant is only used in dead code in a no-codegen function... and yet we still
         // must make sure that the build fails.
-        let _ = PrintName::<T>::VOID; //~ERROR could not evaluate static initializer
+        let _ = PrintName::<T>::VOID; //~ constant
     }
 }
 
diff --git a/src/test/ui/consts/const-eval/erroneous-const.stderr b/src/test/ui/consts/const-eval/erroneous-const.stderr
index 33579135d7c..03030392a51 100644
--- a/src/test/ui/consts/const-eval/erroneous-const.stderr
+++ b/src/test/ui/consts/const-eval/erroneous-const.stderr
@@ -4,18 +4,12 @@ error[E0080]: evaluation of `PrintName::<i32>::VOID` failed
 LL |     const VOID: () = [()][2];
    |                      ^^^^^^^ index out of bounds: the length is 1 but the index is 2
 
-error[E0080]: could not evaluate static initializer
+note: erroneous constant used
   --> $DIR/erroneous-const.rs:13:17
    |
 LL |         let _ = PrintName::<T>::VOID;
    |                 ^^^^^^^^^^^^^^^^^^^^
-   |                 |
-   |                 referenced constant has errors
-   |                 inside `no_codegen::<i32>` at $DIR/erroneous-const.rs:13:17
-...
-LL | pub static FOO: () = no_codegen::<i32>();
-   |                      ------------------- inside `FOO` at $DIR/erroneous-const.rs:17:22
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/erroneous-const2.rs b/src/test/ui/consts/const-eval/erroneous-const2.rs
index 2fbf7be8835..15c0f910728 100644
--- a/src/test/ui/consts/const-eval/erroneous-const2.rs
+++ b/src/test/ui/consts/const-eval/erroneous-const2.rs
@@ -10,7 +10,7 @@ pub static FOO: () = {
     if false {
         // This bad constant is only used in dead code in a static initializer... and yet we still
         // must make sure that the build fails.
-        let _ = PrintName::<i32>::VOID; //~ERROR could not evaluate static initializer
+        let _ = PrintName::<i32>::VOID; //~ constant
     }
 };
 
diff --git a/src/test/ui/consts/const-eval/erroneous-const2.stderr b/src/test/ui/consts/const-eval/erroneous-const2.stderr
index 630b1cf16ae..8626f4d7833 100644
--- a/src/test/ui/consts/const-eval/erroneous-const2.stderr
+++ b/src/test/ui/consts/const-eval/erroneous-const2.stderr
@@ -4,12 +4,12 @@ error[E0080]: evaluation of `PrintName::<i32>::VOID` failed
 LL |     const VOID: () = [()][2];
    |                      ^^^^^^^ index out of bounds: the length is 1 but the index is 2
 
-error[E0080]: could not evaluate static initializer
+note: erroneous constant used
   --> $DIR/erroneous-const2.rs:13:17
    |
 LL |         let _ = PrintName::<i32>::VOID;
-   |                 ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                 ^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/format.rs b/src/test/ui/consts/const-eval/format.rs
index 3eef0d6c3d4..0d8b7c12d8a 100644
--- a/src/test/ui/consts/const-eval/format.rs
+++ b/src/test/ui/consts/const-eval/format.rs
@@ -1,8 +1,6 @@
 const fn failure() {
     panic!("{:?}", 0);
     //~^ ERROR cannot call non-const formatting macro in constant functions
-    //~| ERROR erroneous constant used
-    //~| ERROR erroneous constant used
 }
 
 const fn print() {
@@ -10,8 +8,6 @@ const fn print() {
     //~^ ERROR cannot call non-const formatting macro in constant functions
     //~| ERROR `Arguments::<'a>::new_v1` is not yet stable as a const fn
     //~| ERROR cannot call non-const fn `_print` in constant functions
-    //~| ERROR erroneous constant used
-    //~| ERROR erroneous constant used
 }
 
 fn main() {}
diff --git a/src/test/ui/consts/const-eval/format.stderr b/src/test/ui/consts/const-eval/format.stderr
index 64c7696486f..4bf39db5874 100644
--- a/src/test/ui/consts/const-eval/format.stderr
+++ b/src/test/ui/consts/const-eval/format.stderr
@@ -8,7 +8,7 @@ LL |     panic!("{:?}", 0);
    = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const formatting macro in constant functions
-  --> $DIR/format.rs:9:22
+  --> $DIR/format.rs:7:22
    |
 LL |     println!("{:?}", 0);
    |                      ^
@@ -17,7 +17,7 @@ LL |     println!("{:?}", 0);
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `Arguments::<'a>::new_v1` is not yet stable as a const fn
-  --> $DIR/format.rs:9:5
+  --> $DIR/format.rs:7:5
    |
 LL |     println!("{:?}", 0);
    |     ^^^^^^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL |     println!("{:?}", 0);
    = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const fn `_print` in constant functions
-  --> $DIR/format.rs:9:5
+  --> $DIR/format.rs:7:5
    |
 LL |     println!("{:?}", 0);
    |     ^^^^^^^^^^^^^^^^^^^
@@ -34,35 +34,62 @@ LL |     println!("{:?}", 0);
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/format.rs:2:12
    |
 LL |     panic!("{:?}", 0);
-   |            ^^^^^^ referenced constant has errors
+   |            ^^^^^^
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
+  --> $DIR/format.rs:2:12
+   |
+LL |     panic!("{:?}", 0);
+   |            ^^^^^^
+
+note: erroneous constant used
   --> $DIR/format.rs:2:20
    |
 LL |     panic!("{:?}", 0);
-   |                    ^ referenced constant has errors
+   |                    ^
    |
-   = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this note originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: erroneous constant used
+  --> $DIR/format.rs:2:20
+   |
+LL |     panic!("{:?}", 0);
+   |                    ^
+   |
+   = note: this note originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: erroneous constant used
-  --> $DIR/format.rs:9:14
+note: erroneous constant used
+  --> $DIR/format.rs:7:14
    |
 LL |     println!("{:?}", 0);
-   |              ^^^^^^ referenced constant has errors
+   |              ^^^^^^
 
-error[E0080]: erroneous constant used
-  --> $DIR/format.rs:9:22
+note: erroneous constant used
+  --> $DIR/format.rs:7:14
    |
 LL |     println!("{:?}", 0);
-   |                      ^ referenced constant has errors
+   |              ^^^^^^
+
+note: erroneous constant used
+  --> $DIR/format.rs:7:22
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+LL |     println!("{:?}", 0);
+   |                      ^
+   |
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: erroneous constant used
+  --> $DIR/format.rs:7:22
+   |
+LL |     println!("{:?}", 0);
+   |                      ^
+   |
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 8 previous errors
+error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0015, E0080.
-For more information about an error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/consts/const-eval/issue-44578.rs b/src/test/ui/consts/const-eval/issue-44578.rs
index 2dbe1c2bd16..e4dcc62302c 100644
--- a/src/test/ui/consts/const-eval/issue-44578.rs
+++ b/src/test/ui/consts/const-eval/issue-44578.rs
@@ -23,6 +23,5 @@ impl Foo for u16 {
 
 fn main() {
     println!("{}", <Bar<u16, u8> as Foo>::AMT);
-    //~^ ERROR evaluation of constant value failed
-    //~| ERROR erroneous constant used
+    //~^ constant
 }
diff --git a/src/test/ui/consts/const-eval/issue-44578.stderr b/src/test/ui/consts/const-eval/issue-44578.stderr
index 963381b5870..0cbf5448000 100644
--- a/src/test/ui/consts/const-eval/issue-44578.stderr
+++ b/src/test/ui/consts/const-eval/issue-44578.stderr
@@ -4,20 +4,36 @@ error[E0080]: evaluation of `<Bar<u16, u8> as Foo>::AMT` failed
 LL |     const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize];
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 1 but the index is 1
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-44578.rs:25:20
    |
 LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/issue-44578.rs:25:20
    |
 LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 3 previous errors
+note: erroneous constant used
+  --> $DIR/issue-44578.rs:25:20
+   |
+LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: erroneous constant used
+  --> $DIR/issue-44578.rs:25:20
+   |
+LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/issue-50814-2.rs b/src/test/ui/consts/const-eval/issue-50814-2.rs
index 49d1d8ff080..53eb7b149f9 100644
--- a/src/test/ui/consts/const-eval/issue-50814-2.rs
+++ b/src/test/ui/consts/const-eval/issue-50814-2.rs
@@ -15,7 +15,7 @@ impl<T: C> Foo<T> for A<T> {
 }
 
 fn foo<T: C>() -> &'static usize {
-    &<A<T> as Foo<T>>::BAR //~ ERROR E0080
+    &<A<T> as Foo<T>>::BAR //~ constant
 }
 
 impl C for () {
diff --git a/src/test/ui/consts/const-eval/issue-50814-2.stderr b/src/test/ui/consts/const-eval/issue-50814-2.stderr
index 6604f2b9f8b..956f7aec9da 100644
--- a/src/test/ui/consts/const-eval/issue-50814-2.stderr
+++ b/src/test/ui/consts/const-eval/issue-50814-2.stderr
@@ -4,11 +4,11 @@ error[E0080]: evaluation of `<A<()> as Foo<()>>::BAR` failed
 LL |     const BAR: usize = [5, 6, 7][T::BOO];
    |                        ^^^^^^^^^^^^^^^^^ index out of bounds: the length is 3 but the index is 42
 
-error[E0080]: evaluation of `foo::<()>` failed
+note: erroneous constant used
   --> $DIR/issue-50814-2.rs:18:6
    |
 LL |     &<A<T> as Foo<T>>::BAR
-   |      ^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |      ^^^^^^^^^^^^^^^^^^^^^
 
 note: the above error was encountered while instantiating `fn foo::<()>`
   --> $DIR/issue-50814-2.rs:30:22
@@ -16,6 +16,6 @@ note: the above error was encountered while instantiating `fn foo::<()>`
 LL |     println!("{:x}", foo::<()>() as *const usize as usize);
    |                      ^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/issue-50814.rs b/src/test/ui/consts/const-eval/issue-50814.rs
index 5a587701f78..374ed1d93df 100644
--- a/src/test/ui/consts/const-eval/issue-50814.rs
+++ b/src/test/ui/consts/const-eval/issue-50814.rs
@@ -9,16 +9,16 @@ impl Unsigned for U8 {
     const MAX: u8 = 0xff;
 }
 
-struct Sum<A,B>(A,B);
+struct Sum<A, B>(A, B);
 
-impl<A: Unsigned, B: Unsigned> Unsigned for Sum<A,B> {
+impl<A: Unsigned, B: Unsigned> Unsigned for Sum<A, B> {
     const MAX: u8 = A::MAX + B::MAX;
     //~^ ERROR evaluation of `<Sum<U8, U8> as Unsigned>::MAX` failed
 }
 
 fn foo<T>(_: T) -> &'static u8 {
-    &Sum::<U8,U8>::MAX
-    //~^ ERROR E0080
+    &Sum::<U8, U8>::MAX
+    //~^ constant
 }
 
 fn main() {
diff --git a/src/test/ui/consts/const-eval/issue-50814.stderr b/src/test/ui/consts/const-eval/issue-50814.stderr
index 46dd2b89fa2..05b6271f4e4 100644
--- a/src/test/ui/consts/const-eval/issue-50814.stderr
+++ b/src/test/ui/consts/const-eval/issue-50814.stderr
@@ -4,11 +4,11 @@ error[E0080]: evaluation of `<Sum<U8, U8> as Unsigned>::MAX` failed
 LL |     const MAX: u8 = A::MAX + B::MAX;
    |                     ^^^^^^^^^^^^^^^ attempt to compute `u8::MAX + u8::MAX`, which would overflow
 
-error[E0080]: evaluation of `foo::<i32>` failed
+note: erroneous constant used
   --> $DIR/issue-50814.rs:20:6
    |
-LL |     &Sum::<U8,U8>::MAX
-   |      ^^^^^^^^^^^^^^^^^ referenced constant has errors
+LL |     &Sum::<U8, U8>::MAX
+   |      ^^^^^^^^^^^^^^^^^^
 
 note: the above error was encountered while instantiating `fn foo::<i32>`
   --> $DIR/issue-50814.rs:25:5
@@ -16,6 +16,6 @@ note: the above error was encountered while instantiating `fn foo::<i32>`
 LL |     foo(0);
    |     ^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs
index d2a840932a5..28edf514402 100644
--- a/src/test/ui/consts/const-eval/panic-assoc-never-type.rs
+++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.rs
@@ -11,6 +11,5 @@ impl PrintName {
 }
 
 fn main() {
-    let _ = PrintName::VOID;
-    //~^ ERROR erroneous constant used [E0080]
+    let _ = PrintName::VOID; //~ constant
 }
diff --git a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr
index 4204d302bf8..7c36a3a426e 100644
--- a/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr
+++ b/src/test/ui/consts/const-eval/panic-assoc-never-type.stderr
@@ -6,12 +6,18 @@ LL |     const VOID: ! = panic!();
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/panic-assoc-never-type.rs:14:13
    |
 LL |     let _ = PrintName::VOID;
-   |             ^^^^^^^^^^^^^^^ referenced constant has errors
+   |             ^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+note: erroneous constant used
+  --> $DIR/panic-assoc-never-type.rs:14:13
+   |
+LL |     let _ = PrintName::VOID;
+   |             ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr
index 6f5c028cbca..e5b5c7a846c 100644
--- a/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr
+++ b/src/test/ui/consts/const-eval/ub-ref-ptr.32bit.stderr
@@ -60,14 +60,14 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
    = help: this code performed an operation that depends on the underlying bytes representing a pointer
    = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-ref-ptr.rs:34:38
    |
 LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
-   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:38:86
+  --> $DIR/ub-ref-ptr.rs:37:86
    |
 LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
    |                                                                                      ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
@@ -75,14 +75,14 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us
    = help: this code performed an operation that depends on the underlying bytes representing a pointer
    = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:38:85
+note: erroneous constant used
+  --> $DIR/ub-ref-ptr.rs:37:85
    |
 LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
-   |                                                                                     ^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                                                                     ^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:42:1
+  --> $DIR/ub-ref-ptr.rs:40:1
    |
 LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated)
@@ -93,7 +93,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:45:1
+  --> $DIR/ub-ref-ptr.rs:43:1
    |
 LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated)
@@ -104,13 +104,13 @@ LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
            }
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:48:41
+  --> $DIR/ub-ref-ptr.rs:46:41
    |
 LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:52:1
+  --> $DIR/ub-ref-ptr.rs:50:1
    |
 LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a function pointer
@@ -121,13 +121,13 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
            }
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:54:38
+  --> $DIR/ub-ref-ptr.rs:52:38
    |
 LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init };
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:57:1
+  --> $DIR/ub-ref-ptr.rs:55:1
    |
 LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xd[noalloc], but expected a function pointer
@@ -138,7 +138,7 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:59:1
+  --> $DIR/ub-ref-ptr.rs:57:1
    |
 LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
    | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered alloc41, but expected a function pointer
@@ -148,6 +148,6 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
                ╾─alloc41─╼                                     │ ╾──╼
            }
 
-error: aborting due to 16 previous errors
+error: aborting due to 14 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr
index 5ffb710d456..607366cabc4 100644
--- a/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr
+++ b/src/test/ui/consts/const-eval/ub-ref-ptr.64bit.stderr
@@ -60,14 +60,14 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
    = help: this code performed an operation that depends on the underlying bytes representing a pointer
    = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-ref-ptr.rs:34:38
    |
 LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
-   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:38:86
+  --> $DIR/ub-ref-ptr.rs:37:86
    |
 LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
    |                                                                                      ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
@@ -75,14 +75,14 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us
    = help: this code performed an operation that depends on the underlying bytes representing a pointer
    = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:38:85
+note: erroneous constant used
+  --> $DIR/ub-ref-ptr.rs:37:85
    |
 LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
-   |                                                                                     ^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                                                                     ^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:42:1
+  --> $DIR/ub-ref-ptr.rs:40:1
    |
 LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated)
@@ -93,7 +93,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:45:1
+  --> $DIR/ub-ref-ptr.rs:43:1
    |
 LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated)
@@ -104,13 +104,13 @@ LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
            }
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:48:41
+  --> $DIR/ub-ref-ptr.rs:46:41
    |
 LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:52:1
+  --> $DIR/ub-ref-ptr.rs:50:1
    |
 LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a function pointer
@@ -121,13 +121,13 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
            }
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/ub-ref-ptr.rs:54:38
+  --> $DIR/ub-ref-ptr.rs:52:38
    |
 LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init };
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:57:1
+  --> $DIR/ub-ref-ptr.rs:55:1
    |
 LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xd[noalloc], but expected a function pointer
@@ -138,7 +138,7 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/ub-ref-ptr.rs:59:1
+  --> $DIR/ub-ref-ptr.rs:57:1
    |
 LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
    | ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered alloc41, but expected a function pointer
@@ -148,6 +148,6 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
                ╾───────alloc41───────╼                         │ ╾──────╼
            }
 
-error: aborting due to 16 previous errors
+error: aborting due to 14 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/ub-ref-ptr.rs b/src/test/ui/consts/const-eval/ub-ref-ptr.rs
index 92049d4c179..a1c81239009 100644
--- a/src/test/ui/consts/const-eval/ub-ref-ptr.rs
+++ b/src/test/ui/consts/const-eval/ub-ref-ptr.rs
@@ -33,11 +33,9 @@ const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
 
 const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
 //~^ ERROR evaluation of constant value failed
-//~| ERROR evaluation of constant value failed
 
 const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
 //~^ ERROR evaluation of constant value failed
-//~| ERROR evaluation of constant value failed
 
 const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
 //~^ ERROR it is undefined behavior to use this value
diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr
index c8b46608d6b..9994c2e5a83 100644
--- a/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr
+++ b/src/test/ui/consts/const-eval/ub-wide-ptr.32bit.stderr
@@ -139,11 +139,11 @@ LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
                ╾─allocN─╼                                     │ ╾──╼
            }
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-wide-ptr.rs:83:40
    |
 LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
-   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:90:1
@@ -156,11 +156,11 @@ LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3
                ╾allocN─╼                                     │ ╾──╼
            }
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-wide-ptr.rs:90:42
    |
 LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]);
-   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:94:1
@@ -173,11 +173,11 @@ LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::tran
                ╾allocN─╼                                     │ ╾──╼
            }
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-wide-ptr.rs:94:42
    |
 LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]);
-   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/ub-wide-ptr.rs:102:1
@@ -292,6 +292,6 @@ error[E0080]: could not evaluate static initializer
 LL |     mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
 
-error: aborting due to 32 previous errors
+error: aborting due to 29 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr
index 70574d2dc3b..06a377d9f7c 100644
--- a/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr
+++ b/src/test/ui/consts/const-eval/ub-wide-ptr.64bit.stderr
@@ -139,11 +139,11 @@ LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
                ╾───────allocN───────╼                         │ ╾──────╼
            }
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-wide-ptr.rs:83:40
    |
 LL | const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
-   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:90:1
@@ -156,11 +156,11 @@ LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3
                ╾──────allocN───────╼                         │ ╾──────╼
            }
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-wide-ptr.rs:90:42
    |
 LL | const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]);
-   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
   --> $DIR/ub-wide-ptr.rs:94:1
@@ -173,11 +173,11 @@ LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::tran
                ╾──────allocN───────╼                         │ ╾──────╼
            }
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/ub-wide-ptr.rs:94:42
    |
 LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]);
-   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/ub-wide-ptr.rs:102:1
@@ -292,6 +292,6 @@ error[E0080]: could not evaluate static initializer
 LL |     mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using allocN as vtable pointer but it does not point to a vtable
 
-error: aborting due to 32 previous errors
+error: aborting due to 29 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/ub-wide-ptr.rs b/src/test/ui/consts/const-eval/ub-wide-ptr.rs
index 65f6f023528..2894ef83188 100644
--- a/src/test/ui/consts/const-eval/ub-wide-ptr.rs
+++ b/src/test/ui/consts/const-eval/ub-wide-ptr.rs
@@ -82,18 +82,18 @@ const SLICE_LENGTH_PTR_BOX: Box<[u8]> = unsafe { mem::transmute((&42u8, &3)) };
 // bad data *inside* the slice
 const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { mem::transmute(3u8) }];
 //~^ ERROR it is undefined behavior to use this value
-//~| ERROR evaluation of constant value failed
+//~| constant
 
 // good MySliceBool
 const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]);
 // bad: sized field is not okay
 const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { mem::transmute(3u8) }, [false]);
 //~^ ERROR it is undefined behavior to use this value
-//~| ERROR evaluation of constant value failed
+//~| constant
 // bad: unsized part is not okay
 const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]);
 //~^ ERROR it is undefined behavior to use this value
-//~| ERROR evaluation of constant value failed
+//~| constant
 
 // # raw slice
 const RAW_SLICE_VALID: *const [u8] = unsafe { mem::transmute((&42u8, 1usize)) }; // ok
diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.rs b/src/test/ui/consts/const-eval/union-const-eval-field.rs
index d88bf2a8479..a94fcbbfa56 100644
--- a/src/test/ui/consts/const-eval/union-const-eval-field.rs
+++ b/src/test/ui/consts/const-eval/union-const-eval-field.rs
@@ -29,7 +29,6 @@ const fn read_field3() -> Field3 {
     //~^ ERROR evaluation of constant value failed
     //~| uninitialized
     FIELD3
-    //~^ ERROR erroneous constant used [E0080]
 }
 
 fn main() {
diff --git a/src/test/ui/consts/const-eval/union-const-eval-field.stderr b/src/test/ui/consts/const-eval/union-const-eval-field.stderr
index 00964489e04..9899c56c0ec 100644
--- a/src/test/ui/consts/const-eval/union-const-eval-field.stderr
+++ b/src/test/ui/consts/const-eval/union-const-eval-field.stderr
@@ -4,12 +4,18 @@ error[E0080]: evaluation of constant value failed
 LL |     const FIELD3: Field3 = unsafe { UNION.field3 };
    |                                     ^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/union-const-eval-field.rs:31:5
    |
 LL |     FIELD3
-   |     ^^^^^^ referenced constant has errors
+   |     ^^^^^^
 
-error: aborting due to 2 previous errors
+note: erroneous constant used
+  --> $DIR/union-const-eval-field.rs:31:5
+   |
+LL |     FIELD3
+   |     ^^^^^^
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-float-bits-reject-conv.rs b/src/test/ui/consts/const-float-bits-reject-conv.rs
index 5bf54fdbb3f..c77e99abbf6 100644
--- a/src/test/ui/consts/const-float-bits-reject-conv.rs
+++ b/src/test/ui/consts/const-float-bits-reject-conv.rs
@@ -1,4 +1,5 @@
 // compile-flags: -Zmir-opt-level=0
+// error-pattern: cannot use f32::to_bits on a NaN
 #![feature(const_float_bits_conv)]
 #![feature(const_float_classify)]
 
@@ -25,22 +26,21 @@ fn f32() {
     // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
     // ...actually, let's just check that these break. :D
     const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
+    //~^ inside
     const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
+    //~^ inside
+
+    // The rest of the code is dead because the constants already fail to evaluate.
 
     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-    //~^ ERROR evaluation of constant value failed
     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-    //~^ ERROR evaluation of constant value failed
 
     // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern.
     // In practice, this seems to only cause a problem on x86, since the most widely used calling
     // convention mandates that floating point values are returned on the x87 FPU stack. See #73328.
-    if !cfg!(target_arch = "x86") {
-        const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-        //~^ ERROR evaluation of constant value failed
-        const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-        //~^ ERROR evaluation of constant value failed
-    }
+    // However, during CTFE we still preserve bit patterns (though that is not a guarantee).
+    const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+    const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
 }
 
 fn f64() {
@@ -48,20 +48,18 @@ fn f64() {
     // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
     // ...actually, let's just check that these break. :D
     const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
+    //~^ inside
     const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+    //~^ inside
+
+    // The rest of the code is dead because the constants already fail to evaluate.
 
     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-    //~^ ERROR evaluation of constant value failed
     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-    //~^ ERROR evaluation of constant value failed
 
     // See comment above.
-    if !cfg!(target_arch = "x86") {
-        const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-        //~^ ERROR evaluation of constant value failed
-        const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-        //~^ ERROR evaluation of constant value failed
-    }
+    const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+    const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
 }
 
 fn main() {
diff --git a/src/test/ui/consts/const-float-bits-reject-conv.stderr b/src/test/ui/consts/const-float-bits-reject-conv.stderr
index b3575f64153..e1ad72416f2 100644
--- a/src/test/ui/consts/const-float-bits-reject-conv.stderr
+++ b/src/test/ui/consts/const-float-bits-reject-conv.stderr
@@ -10,10 +10,10 @@ LL |                     panic!("const-eval error: cannot use f32::to_bits on a
 LL |         unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
    |                  -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
    |
-  ::: $DIR/const-float-bits-reject-conv.rs:27:30
+  ::: $DIR/const-float-bits-reject-conv.rs:28:30
    |
 LL |     const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
-   |                              ------------------ inside `f32::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:27:30
+   |                              ------------------ inside `f32::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:28:30
    |
    = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
@@ -29,36 +29,36 @@ LL |                     panic!("const-eval error: cannot use f32::to_bits on a
 LL |         unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
    |                  -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
    |
-  ::: $DIR/const-float-bits-reject-conv.rs:28:30
+  ::: $DIR/const-float-bits-reject-conv.rs:30:30
    |
 LL |     const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
-   |                              ------------------ inside `f32::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:28:30
+   |                              ------------------ inside `f32::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:30:30
    |
    = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:30:34
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:35:34
    |
 LL |     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^ referenced constant has errors
+   |                                  ^^^^^^^^^^^
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:32:34
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:36:34
    |
 LL |     const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^ referenced constant has errors
+   |                                  ^^^^^^^^^^^
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:39:38
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:42:34
    |
-LL |         const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-   |                                      ^^^^^^^^^^^ referenced constant has errors
+LL |     const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+   |                                  ^^^^^^^^^^^
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:41:38
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:43:34
    |
-LL |         const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-   |                                      ^^^^^^^^^^^ referenced constant has errors
+LL |     const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
+   |                                  ^^^^^^^^^^^
 
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/num/f64.rs:LL:COL
@@ -91,37 +91,37 @@ LL |                     panic!("const-eval error: cannot use f64::to_bits on a
 LL |         unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
    |                  -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
    |
-  ::: $DIR/const-float-bits-reject-conv.rs:51:30
+  ::: $DIR/const-float-bits-reject-conv.rs:52:30
    |
 LL |     const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
-   |                              ------------------ inside `f64::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:51:30
+   |                              ------------------ inside `f64::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:52:30
    |
    = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:53:34
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:57:34
    |
 LL |     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^ referenced constant has errors
+   |                                  ^^^^^^^^^^^
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:55:34
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:58:34
    |
 LL |     const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-   |                                  ^^^^^^^^^^^ referenced constant has errors
+   |                                  ^^^^^^^^^^^
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:60:38
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:61:34
    |
-LL |         const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
-   |                                      ^^^^^^^^^^^ referenced constant has errors
+LL |     const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+   |                                  ^^^^^^^^^^^
 
-error[E0080]: evaluation of constant value failed
-  --> $DIR/const-float-bits-reject-conv.rs:62:38
+note: erroneous constant used
+  --> $DIR/const-float-bits-reject-conv.rs:62:34
    |
-LL |         const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
-   |                                      ^^^^^^^^^^^ referenced constant has errors
+LL |     const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
+   |                                  ^^^^^^^^^^^
 
-error: aborting due to 12 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-integer-bool-ops.rs b/src/test/ui/consts/const-integer-bool-ops.rs
index 6924956bdf7..4110ae3e456 100644
--- a/src/test/ui/consts/const-integer-bool-ops.rs
+++ b/src/test/ui/consts/const-integer-bool-ops.rs
@@ -6,7 +6,7 @@ const X: usize = 42 && 39;
 //~| ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARR: [i32; X] = [99; 34];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const X1: usize = 42 || 39;
 //~^ ERROR mismatched types
@@ -16,7 +16,7 @@ const X1: usize = 42 || 39;
 //~| ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARR1: [i32; X1] = [99; 47];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const X2: usize = -42 || -39;
 //~^ ERROR mismatched types
@@ -26,7 +26,7 @@ const X2: usize = -42 || -39;
 //~| ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARR2: [i32; X2] = [99; 18446744073709551607];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const X3: usize = -42 && -39;
 //~^ ERROR mismatched types
@@ -36,43 +36,43 @@ const X3: usize = -42 && -39;
 //~| ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARR3: [i32; X3] = [99; 6];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const Y: usize = 42.0 == 42.0;
 //~^ ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARRR: [i32; Y] = [99; 1];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const Y1: usize = 42.0 >= 42.0;
 //~^ ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARRR1: [i32; Y1] = [99; 1];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const Y2: usize = 42.0 <= 42.0;
 //~^ ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARRR2: [i32; Y2] = [99; 1];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const Y3: usize = 42.0 > 42.0;
 //~^ ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARRR3: [i32; Y3] = [99; 0];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const Y4: usize = 42.0 < 42.0;
 //~^ ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARRR4: [i32; Y4] = [99; 0];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 const Y5: usize = 42.0 != 42.0;
 //~^ ERROR mismatched types
 //~| expected `usize`, found `bool`
 const ARRR5: [i32; Y5] = [99; 0];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 fn main() {
     let _ = ARR;
diff --git a/src/test/ui/consts/const-integer-bool-ops.stderr b/src/test/ui/consts/const-integer-bool-ops.stderr
index 9001fefd102..b5c3b22fdbe 100644
--- a/src/test/ui/consts/const-integer-bool-ops.stderr
+++ b/src/test/ui/consts/const-integer-bool-ops.stderr
@@ -16,11 +16,11 @@ error[E0308]: mismatched types
 LL | const X: usize = 42 && 39;
    |                  ^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:8:18
    |
 LL | const ARR: [i32; X] = [99; 34];
-   |                  ^ referenced constant has errors
+   |                  ^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:11:19
@@ -40,11 +40,11 @@ error[E0308]: mismatched types
 LL | const X1: usize = 42 || 39;
    |                   ^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:18:19
    |
 LL | const ARR1: [i32; X1] = [99; 47];
-   |                   ^^ referenced constant has errors
+   |                   ^^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:21:19
@@ -64,11 +64,11 @@ error[E0308]: mismatched types
 LL | const X2: usize = -42 || -39;
    |                   ^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:28:19
    |
 LL | const ARR2: [i32; X2] = [99; 18446744073709551607];
-   |                   ^^ referenced constant has errors
+   |                   ^^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:31:19
@@ -88,11 +88,11 @@ error[E0308]: mismatched types
 LL | const X3: usize = -42 && -39;
    |                   ^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:38:19
    |
 LL | const ARR3: [i32; X3] = [99; 6];
-   |                   ^^ referenced constant has errors
+   |                   ^^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:41:18
@@ -100,11 +100,11 @@ error[E0308]: mismatched types
 LL | const Y: usize = 42.0 == 42.0;
    |                  ^^^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:44:19
    |
 LL | const ARRR: [i32; Y] = [99; 1];
-   |                   ^ referenced constant has errors
+   |                   ^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:47:19
@@ -112,11 +112,11 @@ error[E0308]: mismatched types
 LL | const Y1: usize = 42.0 >= 42.0;
    |                   ^^^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:50:20
    |
 LL | const ARRR1: [i32; Y1] = [99; 1];
-   |                    ^^ referenced constant has errors
+   |                    ^^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:53:19
@@ -124,11 +124,11 @@ error[E0308]: mismatched types
 LL | const Y2: usize = 42.0 <= 42.0;
    |                   ^^^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:56:20
    |
 LL | const ARRR2: [i32; Y2] = [99; 1];
-   |                    ^^ referenced constant has errors
+   |                    ^^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:59:19
@@ -136,11 +136,11 @@ error[E0308]: mismatched types
 LL | const Y3: usize = 42.0 > 42.0;
    |                   ^^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:62:20
    |
 LL | const ARRR3: [i32; Y3] = [99; 0];
-   |                    ^^ referenced constant has errors
+   |                    ^^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:65:19
@@ -148,11 +148,11 @@ error[E0308]: mismatched types
 LL | const Y4: usize = 42.0 < 42.0;
    |                   ^^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:68:20
    |
 LL | const ARRR4: [i32; Y4] = [99; 0];
-   |                    ^^ referenced constant has errors
+   |                    ^^
 
 error[E0308]: mismatched types
   --> $DIR/const-integer-bool-ops.rs:71:19
@@ -160,13 +160,12 @@ error[E0308]: mismatched types
 LL | const Y5: usize = 42.0 != 42.0;
    |                   ^^^^^^^^^^^^ expected `usize`, found `bool`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-integer-bool-ops.rs:74:20
    |
 LL | const ARRR5: [i32; Y5] = [99; 0];
-   |                    ^^ referenced constant has errors
+   |                    ^^
 
-error: aborting due to 28 previous errors
+error: aborting due to 18 previous errors
 
-Some errors have detailed explanations: E0080, E0308.
-For more information about an error, try `rustc --explain E0080`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/consts/const-len-underflow-separate-spans.rs b/src/test/ui/consts/const-len-underflow-separate-spans.rs
index 478761aef2f..4544c8876ae 100644
--- a/src/test/ui/consts/const-len-underflow-separate-spans.rs
+++ b/src/test/ui/consts/const-len-underflow-separate-spans.rs
@@ -9,5 +9,5 @@ const LEN: usize = ONE - TWO;
 
 fn main() {
     let a: [i8; LEN] = unimplemented!();
-//~^ ERROR E0080
+//~^ constant
 }
diff --git a/src/test/ui/consts/const-len-underflow-separate-spans.stderr b/src/test/ui/consts/const-len-underflow-separate-spans.stderr
index 1416e695e4c..269553631cc 100644
--- a/src/test/ui/consts/const-len-underflow-separate-spans.stderr
+++ b/src/test/ui/consts/const-len-underflow-separate-spans.stderr
@@ -4,12 +4,12 @@ error[E0080]: evaluation of constant value failed
 LL | const LEN: usize = ONE - TWO;
    |                    ^^^^^^^^^ attempt to compute `1_usize - 2_usize`, which would overflow
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-len-underflow-separate-spans.rs:11:17
    |
 LL |     let a: [i8; LEN] = unimplemented!();
-   |                 ^^^ referenced constant has errors
+   |                 ^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr b/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr
index 0f420ae1b6b..109d15a8e4d 100644
--- a/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr
+++ b/src/test/ui/consts/const-mut-refs/issue-76510.32bit.stderr
@@ -19,13 +19,13 @@ error[E0596]: cannot borrow data in a `&` reference as mutable
 LL | const S: &'static mut str = &mut " hello ";
    |                             ^^^^^^^^^^^^^^ cannot borrow as mutable
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-76510.rs:11:70
    |
 LL |         let s = transmute::<(*const u8, usize), &ManuallyDrop<str>>((S.as_ptr(), 3));
-   |                                                                      ^ referenced constant has errors
+   |                                                                      ^
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0080, E0596, E0658, E0764.
-For more information about an error, try `rustc --explain E0080`.
+Some errors have detailed explanations: E0596, E0658, E0764.
+For more information about an error, try `rustc --explain E0596`.
diff --git a/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr b/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr
index 0f420ae1b6b..109d15a8e4d 100644
--- a/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr
+++ b/src/test/ui/consts/const-mut-refs/issue-76510.64bit.stderr
@@ -19,13 +19,13 @@ error[E0596]: cannot borrow data in a `&` reference as mutable
 LL | const S: &'static mut str = &mut " hello ";
    |                             ^^^^^^^^^^^^^^ cannot borrow as mutable
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-76510.rs:11:70
    |
 LL |         let s = transmute::<(*const u8, usize), &ManuallyDrop<str>>((S.as_ptr(), 3));
-   |                                                                      ^ referenced constant has errors
+   |                                                                      ^
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0080, E0596, E0658, E0764.
-For more information about an error, try `rustc --explain E0080`.
+Some errors have detailed explanations: E0596, E0658, E0764.
+For more information about an error, try `rustc --explain E0596`.
diff --git a/src/test/ui/consts/const-mut-refs/issue-76510.rs b/src/test/ui/consts/const-mut-refs/issue-76510.rs
index 08cf64ee330..b853e2737f1 100644
--- a/src/test/ui/consts/const-mut-refs/issue-76510.rs
+++ b/src/test/ui/consts/const-mut-refs/issue-76510.rs
@@ -9,7 +9,7 @@ const S: &'static mut str = &mut " hello ";
 
 const fn trigger() -> [(); unsafe {
         let s = transmute::<(*const u8, usize), &ManuallyDrop<str>>((S.as_ptr(), 3));
-        //~^ ERROR evaluation of constant value failed
+        //~^ constant
         0
     }] {
     [(); 0]
diff --git a/src/test/ui/consts/const-tup-index-span.rs b/src/test/ui/consts/const-tup-index-span.rs
index 763263c6aeb..778a212249c 100644
--- a/src/test/ui/consts/const-tup-index-span.rs
+++ b/src/test/ui/consts/const-tup-index-span.rs
@@ -4,7 +4,7 @@ const TUP: (usize,) = 5usize << 64;
 //~^ ERROR mismatched types
 //~| expected tuple, found `usize`
 const ARR: [i32; TUP.0] = [];
-//~^ ERROR evaluation of constant value failed
+//~^ constant
 
 fn main() {
 }
diff --git a/src/test/ui/consts/const-tup-index-span.stderr b/src/test/ui/consts/const-tup-index-span.stderr
index b178e05e27a..ad846805617 100644
--- a/src/test/ui/consts/const-tup-index-span.stderr
+++ b/src/test/ui/consts/const-tup-index-span.stderr
@@ -11,13 +11,12 @@ help: use a trailing comma to create a tuple with one element
 LL | const TUP: (usize,) = (5usize << 64,);
    |                       +            ++
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/const-tup-index-span.rs:6:18
    |
 LL | const ARR: [i32; TUP.0] = [];
-   |                  ^^^ referenced constant has errors
+   |                  ^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0080, E0308.
-For more information about an error, try `rustc --explain E0080`.
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/consts/extra-const-ub/detect-extra-ub.rs b/src/test/ui/consts/extra-const-ub/detect-extra-ub.rs
index 159cdf257b1..9c239c8a100 100644
--- a/src/test/ui/consts/extra-const-ub/detect-extra-ub.rs
+++ b/src/test/ui/consts/extra-const-ub/detect-extra-ub.rs
@@ -29,7 +29,7 @@ const UNALIGNED_PTR: () = unsafe {
 };
 
 const UNALIGNED_READ: () = {
-    INNER; //[with_flag]~ERROR evaluation of constant value failed
+    INNER; //[with_flag]~ constant
     // There is an error here but its span is in the standard library so we cannot match it...
     // so we have this in a *nested* const, such that the *outer* const fails to use it.
     const INNER: () = unsafe {
diff --git a/src/test/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/src/test/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr
index 3e119582291..4726905ade3 100644
--- a/src/test/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr
+++ b/src/test/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr
@@ -47,12 +47,12 @@ LL |         unsafe { read(self) }
 LL |         ptr.read();
    |         ---------- inside `INNER` at $DIR/detect-extra-ub.rs:38:9
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/detect-extra-ub.rs:32:5
    |
 LL |     INNER;
-   |     ^^^^^ referenced constant has errors
+   |     ^^^^^
 
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/invalid-inline-const-in-match-arm.rs b/src/test/ui/consts/invalid-inline-const-in-match-arm.rs
new file mode 100644
index 00000000000..4d2d8fb1303
--- /dev/null
+++ b/src/test/ui/consts/invalid-inline-const-in-match-arm.rs
@@ -0,0 +1,9 @@
+#![allow(incomplete_features)]
+#![feature(inline_const_pat)]
+
+fn main() {
+    match () {
+        const { (|| {})() } => {}
+        //~^ ERROR cannot call non-const closure in constants
+    }
+}
diff --git a/src/test/ui/consts/invalid-inline-const-in-match-arm.stderr b/src/test/ui/consts/invalid-inline-const-in-match-arm.stderr
new file mode 100644
index 00000000000..ab594c921f9
--- /dev/null
+++ b/src/test/ui/consts/invalid-inline-const-in-match-arm.stderr
@@ -0,0 +1,12 @@
+error[E0015]: cannot call non-const closure in constants
+  --> $DIR/invalid-inline-const-in-match-arm.rs:6:17
+   |
+LL |         const { (|| {})() } => {}
+   |                 ^^^^^^^^^
+   |
+   = note: closures need an RFC before allowed to be called in constants
+   = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/consts/invalid-union.32bit.stderr b/src/test/ui/consts/invalid-union.32bit.stderr
index bad07989e40..4758ea4ae88 100644
--- a/src/test/ui/consts/invalid-union.32bit.stderr
+++ b/src/test/ui/consts/invalid-union.32bit.stderr
@@ -9,12 +9,24 @@ LL | fn main() {
                ╾─alloc7──╼                                     │ ╾──╼
            }
 
-error[E0080]: erroneous constant used
-  --> $DIR/invalid-union.rs:42:25
+note: erroneous constant used
+  --> $DIR/invalid-union.rs:43:25
    |
 LL |     let _: &'static _ = &C;
-   |                         ^^ referenced constant has errors
+   |                         ^^
 
-error: aborting due to 2 previous errors
+note: erroneous constant used
+  --> $DIR/invalid-union.rs:43:25
+   |
+LL |     let _: &'static _ = &C;
+   |                         ^^
+
+note: erroneous constant used
+  --> $DIR/invalid-union.rs:43:25
+   |
+LL |     let _: &'static _ = &C;
+   |                         ^^
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/invalid-union.64bit.stderr b/src/test/ui/consts/invalid-union.64bit.stderr
index a209f0038cc..22b85d20ce9 100644
--- a/src/test/ui/consts/invalid-union.64bit.stderr
+++ b/src/test/ui/consts/invalid-union.64bit.stderr
@@ -9,12 +9,24 @@ LL | fn main() {
                ╾───────alloc7────────╼                         │ ╾──────╼
            }
 
-error[E0080]: erroneous constant used
-  --> $DIR/invalid-union.rs:42:25
+note: erroneous constant used
+  --> $DIR/invalid-union.rs:43:25
    |
 LL |     let _: &'static _ = &C;
-   |                         ^^ referenced constant has errors
+   |                         ^^
 
-error: aborting due to 2 previous errors
+note: erroneous constant used
+  --> $DIR/invalid-union.rs:43:25
+   |
+LL |     let _: &'static _ = &C;
+   |                         ^^
+
+note: erroneous constant used
+  --> $DIR/invalid-union.rs:43:25
+   |
+LL |     let _: &'static _ = &C;
+   |                         ^^
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/invalid-union.rs b/src/test/ui/consts/invalid-union.rs
index 435d26d6e99..28706b4a923 100644
--- a/src/test/ui/consts/invalid-union.rs
+++ b/src/test/ui/consts/invalid-union.rs
@@ -39,5 +39,6 @@ const C: S = {
 };
 
 fn main() { //~ ERROR it is undefined behavior to use this value
-    let _: &'static _ = &C; //~ ERROR erroneous constant used
+    // FIXME the span here is wrong, sould be pointing at the line below, not above.
+    let _: &'static _ = &C;
 }
diff --git a/src/test/ui/consts/issue-36163.stderr b/src/test/ui/consts/issue-36163.stderr
index 9ac6c984cb0..7137c053847 100644
--- a/src/test/ui/consts/issue-36163.stderr
+++ b/src/test/ui/consts/issue-36163.stderr
@@ -5,10 +5,10 @@ LL |     B = A,
    |         ^
    |
 note: ...which requires const-evaluating + checking `A`...
-  --> $DIR/issue-36163.rs:1:1
+  --> $DIR/issue-36163.rs:1:18
    |
 LL | const A: isize = Foo::B as isize;
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                  ^^^^^^^^^^^^^^^
    = note: ...which again requires const-evaluating + checking `Foo::B::{constant#0}`, completing the cycle
 note: cycle used when simplifying constant for the type system `Foo::B::{constant#0}`
   --> $DIR/issue-36163.rs:4:9
diff --git a/src/test/ui/consts/issue-54954.rs b/src/test/ui/consts/issue-54954.rs
index d4e1df22770..520bf508ff3 100644
--- a/src/test/ui/consts/issue-54954.rs
+++ b/src/test/ui/consts/issue-54954.rs
@@ -9,8 +9,8 @@ trait Tt {
 }
 
 fn f(z: [f32; ARR_LEN]) -> [f32; ARR_LEN] {
-  //~^ ERROR evaluation of constant value failed
-  //~| ERROR evaluation of constant value failed
+  //~^ constant
+  //~| constant
     z
 }
 
diff --git a/src/test/ui/consts/issue-54954.stderr b/src/test/ui/consts/issue-54954.stderr
index 668985c2b59..85055828737 100644
--- a/src/test/ui/consts/issue-54954.stderr
+++ b/src/test/ui/consts/issue-54954.stderr
@@ -16,19 +16,19 @@ LL | |         core::mem::size_of::<T>()
 LL | |     }
    | |_____- `Tt::const_val` defined here
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-54954.rs:11:15
    |
 LL | fn f(z: [f32; ARR_LEN]) -> [f32; ARR_LEN] {
-   |               ^^^^^^^ referenced constant has errors
+   |               ^^^^^^^
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-54954.rs:11:34
    |
 LL | fn f(z: [f32; ARR_LEN]) -> [f32; ARR_LEN] {
-   |                                  ^^^^^^^ referenced constant has errors
+   |                                  ^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0080, E0379, E0790.
-For more information about an error, try `rustc --explain E0080`.
+Some errors have detailed explanations: E0379, E0790.
+For more information about an error, try `rustc --explain E0379`.
diff --git a/src/test/ui/consts/issue-56164.rs b/src/test/ui/consts/issue-56164.rs
index df3e3bf9028..22c257d0b08 100644
--- a/src/test/ui/consts/issue-56164.rs
+++ b/src/test/ui/consts/issue-56164.rs
@@ -1,6 +1,5 @@
 const fn foo() { (||{})() }
 //~^ ERROR cannot call non-const closure
-//~| ERROR erroneous constant used
 
 const fn bad(input: fn()) {
     input()
diff --git a/src/test/ui/consts/issue-56164.stderr b/src/test/ui/consts/issue-56164.stderr
index c5b2c57fbee..2579b3e7827 100644
--- a/src/test/ui/consts/issue-56164.stderr
+++ b/src/test/ui/consts/issue-56164.stderr
@@ -8,18 +8,23 @@ LL | const fn foo() { (||{})() }
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
 
 error: function pointer calls are not allowed in constant functions
-  --> $DIR/issue-56164.rs:6:5
+  --> $DIR/issue-56164.rs:5:5
    |
 LL |     input()
    |     ^^^^^^^
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/issue-56164.rs:1:18
    |
 LL | const fn foo() { (||{})() }
-   |                  ^^^^^^ referenced constant has errors
+   |                  ^^^^^^
 
-error: aborting due to 3 previous errors
+note: erroneous constant used
+  --> $DIR/issue-56164.rs:1:18
+   |
+LL | const fn foo() { (||{})() }
+   |                  ^^^^^^
+
+error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0015, E0080.
-For more information about an error, try `rustc --explain E0015`.
+For more information about this error, try `rustc --explain E0015`.
diff --git a/src/test/ui/consts/issue-66693.rs b/src/test/ui/consts/issue-66693.rs
index 1ff250be1b0..df45d01ec02 100644
--- a/src/test/ui/consts/issue-66693.rs
+++ b/src/test/ui/consts/issue-66693.rs
@@ -10,7 +10,6 @@ static _FOO: () = panic!(true);
 const fn _foo() {
     panic!(&1);
     //~^ ERROR: argument to `panic!()` in a const context must have type `&str`
-    //~| ERROR: erroneous constant used
 }
 
 // ensure that conforming panics don't cause an error
diff --git a/src/test/ui/consts/issue-66693.stderr b/src/test/ui/consts/issue-66693.stderr
index 911374f507e..e9a3fced61c 100644
--- a/src/test/ui/consts/issue-66693.stderr
+++ b/src/test/ui/consts/issue-66693.stderr
@@ -22,12 +22,17 @@ LL |     panic!(&1);
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/issue-66693.rs:11:12
    |
 LL |     panic!(&1);
-   |            ^^ referenced constant has errors
+   |            ^^
 
-error: aborting due to 4 previous errors
+note: erroneous constant used
+  --> $DIR/issue-66693.rs:11:12
+   |
+LL |     panic!(&1);
+   |            ^^
+
+error: aborting due to 3 previous errors
 
-For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/miri_unleashed/assoc_const.rs b/src/test/ui/consts/miri_unleashed/assoc_const.rs
index 76ed667a514..7bb0c1b772a 100644
--- a/src/test/ui/consts/miri_unleashed/assoc_const.rs
+++ b/src/test/ui/consts/miri_unleashed/assoc_const.rs
@@ -26,5 +26,5 @@ fn main() {
     // this is fine, but would have been forbidden by the static checks on `F`
     let x = <() as Bar<u32, ()>>::F;
     // this test only causes errors due to the line below, so post-monomorphization
-    let y = <String as Bar<Vec<u32>, String>>::F; //~ ERROR erroneous constant
+    let y = <String as Bar<Vec<u32>, String>>::F; //~ constant
 }
diff --git a/src/test/ui/consts/miri_unleashed/assoc_const.stderr b/src/test/ui/consts/miri_unleashed/assoc_const.stderr
index 519bd0748e2..33e7e4af276 100644
--- a/src/test/ui/consts/miri_unleashed/assoc_const.stderr
+++ b/src/test/ui/consts/miri_unleashed/assoc_const.stderr
@@ -13,11 +13,23 @@ LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
 LL |     const F: u32 = (U::X, 42).1;
    |                               - inside `<String as Bar<Vec<u32>, String>>::F` at $DIR/assoc_const.rs:12:31
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/assoc_const.rs:29:13
    |
 LL |     let y = <String as Bar<Vec<u32>, String>>::F;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: erroneous constant used
+  --> $DIR/assoc_const.rs:29:13
+   |
+LL |     let y = <String as Bar<Vec<u32>, String>>::F;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: erroneous constant used
+  --> $DIR/assoc_const.rs:29:13
+   |
+LL |     let y = <String as Bar<Vec<u32>, String>>::F;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: skipping const checks
    |
@@ -27,6 +39,6 @@ help: skipping check that does not even have a feature gate
 LL |     const F: u32 = (U::X, 42).1;
    |                    ^^^^^^^^^^
 
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to previous error; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/miri_unleashed/assoc_const_2.rs b/src/test/ui/consts/miri_unleashed/assoc_const_2.rs
index 8377141ea5e..aad5b34606e 100644
--- a/src/test/ui/consts/miri_unleashed/assoc_const_2.rs
+++ b/src/test/ui/consts/miri_unleashed/assoc_const_2.rs
@@ -24,5 +24,5 @@ impl Bar<String> for String {}
 fn main() {
     let x = <() as Bar<()>>::F;
     // this test only causes errors due to the line below, so post-monomorphization
-    let y = <String as Bar<String>>::F; //~ ERROR erroneous constant
+    let y = <String as Bar<String>>::F; //~ constant
 }
diff --git a/src/test/ui/consts/miri_unleashed/assoc_const_2.stderr b/src/test/ui/consts/miri_unleashed/assoc_const_2.stderr
index 2bf753c2ba3..fc4b18056da 100644
--- a/src/test/ui/consts/miri_unleashed/assoc_const_2.stderr
+++ b/src/test/ui/consts/miri_unleashed/assoc_const_2.stderr
@@ -4,12 +4,24 @@ error[E0080]: evaluation of `<std::string::String as Bar<std::string::String>>::
 LL |     const F: u32 = 100 / U::X;
    |                    ^^^^^^^^^^ attempt to divide `100_u32` by zero
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/assoc_const_2.rs:27:13
    |
 LL |     let y = <String as Bar<String>>::F;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+note: erroneous constant used
+  --> $DIR/assoc_const_2.rs:27:13
+   |
+LL |     let y = <String as Bar<String>>::F;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: erroneous constant used
+  --> $DIR/assoc_const_2.rs:27:13
+   |
+LL |     let y = <String as Bar<String>>::F;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.rs b/src/test/ui/consts/uninhabited-const-issue-61744.rs
index a07c3988277..ca6449cce30 100644
--- a/src/test/ui/consts/uninhabited-const-issue-61744.rs
+++ b/src/test/ui/consts/uninhabited-const-issue-61744.rs
@@ -15,5 +15,5 @@ trait Const {
 impl<T> Const for T {}
 
 pub fn main() -> () {
-    dbg!(i32::CONSTANT); //~ ERROR erroneous constant used
+    dbg!(i32::CONSTANT); //~ constant
 }
diff --git a/src/test/ui/consts/uninhabited-const-issue-61744.stderr b/src/test/ui/consts/uninhabited-const-issue-61744.stderr
index 9c7cc88613e..8b39f390bb4 100644
--- a/src/test/ui/consts/uninhabited-const-issue-61744.stderr
+++ b/src/test/ui/consts/uninhabited-const-issue-61744.stderr
@@ -140,12 +140,24 @@ LL |     fake_type()
 LL |     const CONSTANT: i32 = unsafe { fake_type() };
    |                                    ----------- inside `<i32 as Const>::CONSTANT` at $DIR/uninhabited-const-issue-61744.rs:12:36
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/uninhabited-const-issue-61744.rs:18:10
    |
 LL |     dbg!(i32::CONSTANT);
-   |          ^^^^^^^^^^^^^ referenced constant has errors
+   |          ^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+note: erroneous constant used
+  --> $DIR/uninhabited-const-issue-61744.rs:18:10
+   |
+LL |     dbg!(i32::CONSTANT);
+   |          ^^^^^^^^^^^^^
+
+note: erroneous constant used
+  --> $DIR/uninhabited-const-issue-61744.rs:18:10
+   |
+LL |     dbg!(i32::CONSTANT);
+   |          ^^^^^^^^^^^^^
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/dyn-star/issue-102430.rs b/src/test/ui/dyn-star/issue-102430.rs
new file mode 100644
index 00000000000..244ecda6626
--- /dev/null
+++ b/src/test/ui/dyn-star/issue-102430.rs
@@ -0,0 +1,32 @@
+// check-pass
+
+#![feature(dyn_star)]
+#![allow(incomplete_features)]
+
+trait AddOne {
+    fn add1(&mut self) -> usize;
+}
+
+impl AddOne for usize {
+    fn add1(&mut self) -> usize {
+        *self += 1;
+        *self
+    }
+}
+
+impl AddOne for &mut usize {
+    fn add1(&mut self) -> usize {
+        (*self).add1()
+    }
+}
+
+fn add_one(mut i: dyn* AddOne + '_) -> usize {
+    i.add1()
+}
+
+fn main() {
+    let mut x = 42usize;
+    let y = &mut x as (dyn* AddOne + '_);
+
+    println!("{}", add_one(y));
+}
diff --git a/src/test/ui/editions/edition-keywords-2015-2015-parsing.stderr b/src/test/ui/editions/edition-keywords-2015-2015-parsing.stderr
index 3435fdfd725..39944622d07 100644
--- a/src/test/ui/editions/edition-keywords-2015-2015-parsing.stderr
+++ b/src/test/ui/editions/edition-keywords-2015-2015-parsing.stderr
@@ -3,12 +3,24 @@ error: no rules expected the token `r#async`
    |
 LL |     r#async = consumes_async!(r#async);
    |                               ^^^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `async`
+  --> $DIR/auxiliary/edition-kw-macro-2015.rs:17:6
+   |
+LL |     (async) => (1)
+   |      ^^^^^
 
 error: no rules expected the token `async`
   --> $DIR/edition-keywords-2015-2015-parsing.rs:17:35
    |
 LL |     r#async = consumes_async_raw!(async);
    |                                   ^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `r#async`
+  --> $DIR/auxiliary/edition-kw-macro-2015.rs:22:6
+   |
+LL |     (r#async) => (1)
+   |      ^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/editions/edition-keywords-2015-2018-parsing.stderr b/src/test/ui/editions/edition-keywords-2015-2018-parsing.stderr
index 6e86d746f54..fa83908e666 100644
--- a/src/test/ui/editions/edition-keywords-2015-2018-parsing.stderr
+++ b/src/test/ui/editions/edition-keywords-2015-2018-parsing.stderr
@@ -3,12 +3,24 @@ error: no rules expected the token `r#async`
    |
 LL |     r#async = consumes_async!(r#async);
    |                               ^^^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `async`
+  --> $DIR/auxiliary/edition-kw-macro-2018.rs:17:6
+   |
+LL |     (async) => (1)
+   |      ^^^^^
 
 error: no rules expected the token `async`
   --> $DIR/edition-keywords-2015-2018-parsing.rs:17:35
    |
 LL |     r#async = consumes_async_raw!(async);
    |                                   ^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `r#async`
+  --> $DIR/auxiliary/edition-kw-macro-2018.rs:22:6
+   |
+LL |     (r#async) => (1)
+   |      ^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr
index e1eea725bb0..1a4a94e9733 100644
--- a/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr
+++ b/src/test/ui/editions/edition-keywords-2018-2015-parsing.stderr
@@ -25,12 +25,24 @@ error: no rules expected the token `r#async`
    |
 LL |     r#async = consumes_async!(r#async);
    |                               ^^^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `async`
+  --> $DIR/auxiliary/edition-kw-macro-2015.rs:17:6
+   |
+LL |     (async) => (1)
+   |      ^^^^^
 
 error: no rules expected the token `async`
   --> $DIR/edition-keywords-2018-2015-parsing.rs:21:35
    |
 LL |     r#async = consumes_async_raw!(async);
    |                                   ^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `r#async`
+  --> $DIR/auxiliary/edition-kw-macro-2015.rs:22:6
+   |
+LL |     (r#async) => (1)
+   |      ^^^^^^^
 
 error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
   --> $DIR/auxiliary/edition-kw-macro-2015.rs:27:23
diff --git a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr
index 0af4da09c19..19eb7ac9823 100644
--- a/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr
+++ b/src/test/ui/editions/edition-keywords-2018-2018-parsing.stderr
@@ -25,12 +25,24 @@ error: no rules expected the token `r#async`
    |
 LL |     r#async = consumes_async!(r#async);
    |                               ^^^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `async`
+  --> $DIR/auxiliary/edition-kw-macro-2018.rs:17:6
+   |
+LL |     (async) => (1)
+   |      ^^^^^
 
 error: no rules expected the token `async`
   --> $DIR/edition-keywords-2018-2018-parsing.rs:21:35
    |
 LL |     r#async = consumes_async_raw!(async);
    |                                   ^^^^^ no rules expected this token in macro call
+   |
+note: while trying to match `r#async`
+  --> $DIR/auxiliary/edition-kw-macro-2018.rs:22:6
+   |
+LL |     (r#async) => (1)
+   |      ^^^^^^^
 
 error: macro expansion ends with an incomplete expression: expected one of `move`, `|`, or `||`
   --> $DIR/auxiliary/edition-kw-macro-2018.rs:27:23
diff --git a/src/test/ui/empty/empty-comment.stderr b/src/test/ui/empty/empty-comment.stderr
index f583dbbdc64..7cc8d8fe922 100644
--- a/src/test/ui/empty/empty-comment.stderr
+++ b/src/test/ui/empty/empty-comment.stderr
@@ -6,6 +6,12 @@ LL | macro_rules! one_arg_macro {
 ...
 LL |     one_arg_macro!(/**/);
    |     ^^^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$fmt:expr`
+  --> $DIR/empty-comment.rs:6:6
+   |
+LL |     ($fmt:expr) => (print!(concat!($fmt, "\n")));
+   |      ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/e0119/conflict-with-std.stderr b/src/test/ui/error-codes/e0119/conflict-with-std.stderr
index 3ff96a6a4d6..ef888a1c287 100644
--- a/src/test/ui/error-codes/e0119/conflict-with-std.stderr
+++ b/src/test/ui/error-codes/e0119/conflict-with-std.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `std::convert::AsRef<Q>` for type `std::boxed::Box<Q>`
+error[E0119]: conflicting implementations of trait `AsRef<Q>` for type `Box<Q>`
   --> $DIR/conflict-with-std.rs:5:1
    |
 LL | impl AsRef<Q> for Box<Q> {
@@ -8,7 +8,7 @@ LL | impl AsRef<Q> for Box<Q> {
            - impl<T, A> AsRef<T> for Box<T, A>
              where A: Allocator, T: ?Sized;
 
-error[E0119]: conflicting implementations of trait `std::convert::From<S>` for type `S`
+error[E0119]: conflicting implementations of trait `From<S>` for type `S`
   --> $DIR/conflict-with-std.rs:12:1
    |
 LL | impl From<S> for S {
@@ -17,7 +17,7 @@ LL | impl From<S> for S {
    = note: conflicting implementation in crate `core`:
            - impl<T> From<T> for T;
 
-error[E0119]: conflicting implementations of trait `std::convert::TryFrom<X>` for type `X`
+error[E0119]: conflicting implementations of trait `TryFrom<X>` for type `X`
   --> $DIR/conflict-with-std.rs:19:1
    |
 LL | impl TryFrom<X> for X {
diff --git a/src/test/ui/error-codes/e0119/issue-23563.stderr b/src/test/ui/error-codes/e0119/issue-23563.stderr
index f149cef587f..1b2d64282e1 100644
--- a/src/test/ui/error-codes/e0119/issue-23563.stderr
+++ b/src/test/ui/error-codes/e0119/issue-23563.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `a::LolFrom<&[_]>` for type `LocalType<_>`
+error[E0119]: conflicting implementations of trait `LolFrom<&[_]>` for type `LocalType<_>`
   --> $DIR/issue-23563.rs:13:1
    |
 LL | impl<'a, T> LolFrom<&'a [T]> for LocalType<T> {
diff --git a/src/test/ui/error-codes/e0119/issue-27403.stderr b/src/test/ui/error-codes/e0119/issue-27403.stderr
index c11a5048747..9b3345c23bb 100644
--- a/src/test/ui/error-codes/e0119/issue-27403.stderr
+++ b/src/test/ui/error-codes/e0119/issue-27403.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `std::convert::Into<_>` for type `GenX<_>`
+error[E0119]: conflicting implementations of trait `Into<_>` for type `GenX<_>`
   --> $DIR/issue-27403.rs:5:1
    |
 LL | impl<S> Into<S> for GenX<S> {
diff --git a/src/test/ui/error-codes/e0119/so-37347311.stderr b/src/test/ui/error-codes/e0119/so-37347311.stderr
index f1c2b0d2974..99367e80841 100644
--- a/src/test/ui/error-codes/e0119/so-37347311.stderr
+++ b/src/test/ui/error-codes/e0119/so-37347311.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `std::convert::From<MyError<_>>` for type `MyError<_>`
+error[E0119]: conflicting implementations of trait `From<MyError<_>>` for type `MyError<_>`
   --> $DIR/so-37347311.rs:11:1
    |
 LL | impl<S: Storage> From<S::Error> for MyError<S> {
diff --git a/src/test/ui/fail-simple.stderr b/src/test/ui/fail-simple.stderr
index 26ed918e94d..af8f54291ff 100644
--- a/src/test/ui/fail-simple.stderr
+++ b/src/test/ui/fail-simple.stderr
@@ -3,6 +3,8 @@ error: no rules expected the token `@`
    |
 LL |     panic!(@);
    |            ^ no rules expected this token in macro call
+   |
+   = note: while trying to match end of macro
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/fmt/format-raw-string-error.rs b/src/test/ui/fmt/format-raw-string-error.rs
new file mode 100644
index 00000000000..9f0bc01a749
--- /dev/null
+++ b/src/test/ui/fmt/format-raw-string-error.rs
@@ -0,0 +1,3 @@
+fn main() {
+    println!(r#"\'\'\'\'\'\'\'\'\'\'\'\'\'\'}"#); //~ ERROR invalid format string: unmatched `}` found
+}
diff --git a/src/test/ui/fmt/format-raw-string-error.stderr b/src/test/ui/fmt/format-raw-string-error.stderr
new file mode 100644
index 00000000000..8d61950d8c2
--- /dev/null
+++ b/src/test/ui/fmt/format-raw-string-error.stderr
@@ -0,0 +1,10 @@
+error: invalid format string: unmatched `}` found
+  --> $DIR/format-raw-string-error.rs:2:45
+   |
+LL |     println!(r#"\'\'\'\'\'\'\'\'\'\'\'\'\'\'}"#);
+   |                                             ^ unmatched `}` in format string
+   |
+   = note: if you intended to print `}`, you can escape it using `}}`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/fmt/issue-104142.rs b/src/test/ui/fmt/issue-104142.rs
new file mode 100644
index 00000000000..8d7283a7197
--- /dev/null
+++ b/src/test/ui/fmt/issue-104142.rs
@@ -0,0 +1,6 @@
+fn main() {
+    println!(
+        r#"
+    \"\'}、"# //~ ERROR invalid format string: unmatched `}` found
+    );
+}
diff --git a/src/test/ui/fmt/issue-104142.stderr b/src/test/ui/fmt/issue-104142.stderr
new file mode 100644
index 00000000000..d41644faa28
--- /dev/null
+++ b/src/test/ui/fmt/issue-104142.stderr
@@ -0,0 +1,10 @@
+error: invalid format string: unmatched `}` found
+  --> $DIR/issue-104142.rs:4:9
+   |
+LL |     \"\'}、"#
+   |         ^ unmatched `}` in format string
+   |
+   = note: if you intended to print `}`, you can escape it using `}}`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.rs b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.rs
new file mode 100644
index 00000000000..ab9d9a7ce6f
--- /dev/null
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.rs
@@ -0,0 +1,53 @@
+// check-fail
+// known-bug: #90950
+
+trait Yokeable<'a>: 'static {
+    type Output: 'a;
+}
+
+
+trait IsCovariant<'a> {}
+
+struct Yoke<Y: for<'a> Yokeable<'a>> {
+    data: Y,
+}
+
+
+// impl<Y: for<'a> Yokeable<'a>> Yoke<Y> {
+//     fn project<Y2: for<'a> Yokeable<'a>>(
+//         &self,
+//         f: for<'a> fn(<Y as Yokeable<'a>>::Output, &'a (),
+//     ) -> <Y2 as Yokeable<'a>>::Output) -> Yoke<Y2> {
+//         unimplemented!()
+//     }
+// }
+
+fn upcast<Y>(x: Yoke<Y>) -> Yoke<Box<dyn IsCovariant<'static> + 'static>> where
+    Y: for<'a> Yokeable<'a>,
+    for<'a> <Y as Yokeable<'a>>::Output: IsCovariant<'a>
+    {
+    // x.project(|data, _| {
+    //     Box::new(data)
+    // })
+    unimplemented!()
+}
+
+
+impl<'a> Yokeable<'a> for Box<dyn IsCovariant<'static> + 'static> {
+    type Output = Box<dyn IsCovariant<'a> + 'a>;
+}
+
+// this impl is mostly an example and unnecessary for the pure repro
+use std::borrow::*;
+impl<'a, T: ToOwned + ?Sized> Yokeable<'a> for Cow<'static, T> {
+    type Output = Cow<'a, T>;
+}
+impl<'a, T: ToOwned + ?Sized> IsCovariant<'a> for Cow<'a, T> {}
+
+
+
+fn upcast_yoke(y: Yoke<Cow<'static, str>>) -> Yoke<Box<dyn IsCovariant<'static> + 'static>> {
+    upcast(y)
+}
+
+fn main() {}
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.stderr b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.stderr
new file mode 100644
index 00000000000..6206b167b0b
--- /dev/null
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-90950.stderr
@@ -0,0 +1,21 @@
+error[E0277]: the trait bound `for<'a> <_ as Yokeable<'a>>::Output: IsCovariant<'a>` is not satisfied
+  --> $DIR/issue-90950.rs:50:12
+   |
+LL |     upcast(y)
+   |     ------ ^ the trait `for<'a> IsCovariant<'a>` is not implemented for `<_ as Yokeable<'a>>::Output`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `IsCovariant<'a>` is implemented for `std::borrow::Cow<'a, T>`
+note: required by a bound in `upcast`
+  --> $DIR/issue-90950.rs:27:42
+   |
+LL | fn upcast<Y>(x: Yoke<Y>) -> Yoke<Box<dyn IsCovariant<'static> + 'static>> where
+   |    ------ required by a bound in this
+LL |     Y: for<'a> Yokeable<'a>,
+LL |     for<'a> <Y as Yokeable<'a>>::Output: IsCovariant<'a>
+   |                                          ^^^^^^^^^^^^^^^ required by this bound in `upcast`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.rs b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.rs
new file mode 100644
index 00000000000..7693b118247
--- /dev/null
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.rs
@@ -0,0 +1,23 @@
+// check-fail
+// known-bug: #89196
+
+// Should pass, but we normalize and check bounds before we resolve the generics
+// of the function (which we know because of the return type).
+
+trait Trait<'a> {
+    type Out;
+}
+
+impl<'a, T> Trait<'a> for T {
+    type Out = T;
+}
+
+fn weird_bound<X>() -> X
+    where
+        for<'a> X: Trait<'a>,
+        for<'a> <X as Trait<'a>>::Out: Copy
+{ todo!() }
+
+fn main() {
+    let _: () = weird_bound();
+}
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
new file mode 100644
index 00000000000..51c9646004a
--- /dev/null
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `for<'a> <_ as Trait<'a>>::Out: Copy` is not satisfied
+  --> $DIR/norm-before-method-resolution.rs:22:17
+   |
+LL |     let _: () = weird_bound();
+   |                 ^^^^^^^^^^^ the trait `for<'a> Copy` is not implemented for `<_ as Trait<'a>>::Out`
+   |
+note: required by a bound in `weird_bound`
+  --> $DIR/norm-before-method-resolution.rs:18:40
+   |
+LL | fn weird_bound<X>() -> X
+   |    ----------- required by a bound in this
+...
+LL |         for<'a> <X as Trait<'a>>::Out: Copy
+   |                                        ^^^^ required by this bound in `weird_bound`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/impl-trait/deduce-signature-from-supertrait.rs b/src/test/ui/impl-trait/deduce-signature-from-supertrait.rs
new file mode 100644
index 00000000000..d2c34792035
--- /dev/null
+++ b/src/test/ui/impl-trait/deduce-signature-from-supertrait.rs
@@ -0,0 +1,15 @@
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+
+trait SuperExpectation: Fn(i32) {}
+
+impl<T: Fn(i32)> SuperExpectation for T {}
+
+type Foo = impl SuperExpectation;
+
+fn main() {
+    let _: Foo = |x| {
+        let _ = x.to_string();
+    };
+}
diff --git a/src/test/ui/issues/issue-17252.stderr b/src/test/ui/issues/issue-17252.stderr
index 4856418ed60..aca5242b296 100644
--- a/src/test/ui/issues/issue-17252.stderr
+++ b/src/test/ui/issues/issue-17252.stderr
@@ -1,8 +1,8 @@
 error[E0391]: cycle detected when const-evaluating + checking `FOO`
-  --> $DIR/issue-17252.rs:1:1
+  --> $DIR/issue-17252.rs:1:20
    |
 LL | const FOO: usize = FOO;
-   | ^^^^^^^^^^^^^^^^^^^^^^^
+   |                    ^^^
    |
    = note: ...which immediately requires const-evaluating + checking `FOO` again
 note: cycle used when const-evaluating + checking `main::{constant#0}`
diff --git a/src/test/ui/issues/issue-23302-3.stderr b/src/test/ui/issues/issue-23302-3.stderr
index 074939f68a8..c6cafe575e5 100644
--- a/src/test/ui/issues/issue-23302-3.stderr
+++ b/src/test/ui/issues/issue-23302-3.stderr
@@ -1,14 +1,14 @@
 error[E0391]: cycle detected when const-evaluating + checking `A`
-  --> $DIR/issue-23302-3.rs:1:1
+  --> $DIR/issue-23302-3.rs:1:16
    |
 LL | const A: i32 = B;
-   | ^^^^^^^^^^^^^^^^^
+   |                ^
    |
 note: ...which requires const-evaluating + checking `B`...
-  --> $DIR/issue-23302-3.rs:3:1
+  --> $DIR/issue-23302-3.rs:3:16
    |
 LL | const B: i32 = A;
-   | ^^^^^^^^^^^^^^^^^
+   |                ^
    = note: ...which again requires const-evaluating + checking `A`, completing the cycle
 note: cycle used when simplifying constant for the type system `A`
   --> $DIR/issue-23302-3.rs:1:1
diff --git a/src/test/ui/issues/issue-28568.stderr b/src/test/ui/issues/issue-28568.stderr
index be3f7c62780..960259080f7 100644
--- a/src/test/ui/issues/issue-28568.stderr
+++ b/src/test/ui/issues/issue-28568.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `MyStruct`
+error[E0119]: conflicting implementations of trait `Drop` for type `MyStruct`
   --> $DIR/issue-28568.rs:7:1
    |
 LL | impl Drop for MyStruct {
diff --git a/src/test/ui/issues/issue-30490.rs b/src/test/ui/issues/issue-30490.rs
index 68d9c4de4d1..4f0eeac8f71 100644
--- a/src/test/ui/issues/issue-30490.rs
+++ b/src/test/ui/issues/issue-30490.rs
@@ -10,7 +10,7 @@
 // This test checks to avoid that regression.
 
 #![cfg_attr(unix, feature(rustc_private))]
-#![cfg_attr(windows, allow(unused_imports))]
+#![cfg_attr(not(unix), allow(unused_imports))]
 
 #[cfg(unix)]
 extern crate libc;
diff --git a/src/test/ui/issues/issue-41394.rs b/src/test/ui/issues/issue-41394.rs
index 64873ac35a0..07cad8796e1 100644
--- a/src/test/ui/issues/issue-41394.rs
+++ b/src/test/ui/issues/issue-41394.rs
@@ -5,7 +5,7 @@ enum Foo {
 
 enum Bar {
     A = Foo::A as isize
-    //~^ ERROR evaluation of constant value failed
+    //~^ const
 }
 
 fn main() {}
diff --git a/src/test/ui/issues/issue-41394.stderr b/src/test/ui/issues/issue-41394.stderr
index 47a24547d45..1b5c64628a1 100644
--- a/src/test/ui/issues/issue-41394.stderr
+++ b/src/test/ui/issues/issue-41394.stderr
@@ -6,13 +6,12 @@ LL |     A = "" + 1
    |         |
    |         &str
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-41394.rs:7:9
    |
 LL |     A = Foo::A as isize
-   |         ^^^^^^^^^^^^^^^ referenced constant has errors
+   |         ^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0080, E0369.
-For more information about an error, try `rustc --explain E0080`.
+For more information about this error, try `rustc --explain E0369`.
diff --git a/src/test/ui/issues/issue-43355.stderr b/src/test/ui/issues/issue-43355.stderr
index 531130fecab..57adc8ad5ef 100644
--- a/src/test/ui/issues/issue-43355.stderr
+++ b/src/test/ui/issues/issue-43355.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `Trait1<std::boxed::Box<_>>` for type `A`
+error[E0119]: conflicting implementations of trait `Trait1<Box<_>>` for type `A`
   --> $DIR/issue-43355.rs:13:1
    |
 LL | impl<X, T> Trait1<X> for T where T: Trait2<X> {
diff --git a/src/test/ui/issues/issue-48728.rs b/src/test/ui/issues/issue-48728.rs
index 8405a30478b..cbdc10bd2e1 100644
--- a/src/test/ui/issues/issue-48728.rs
+++ b/src/test/ui/issues/issue-48728.rs
@@ -1,7 +1,7 @@
 // Regression test for #48728, an ICE that occurred computing
 // coherence "help" information.
 
-#[derive(Clone)] //~ ERROR conflicting implementations of trait `std::clone::Clone`
+#[derive(Clone)] //~ ERROR conflicting implementations of trait `Clone`
 struct Node<T: ?Sized>(Box<T>);
 
 impl<T: Clone + ?Sized> Clone for Node<[T]> {
diff --git a/src/test/ui/issues/issue-48728.stderr b/src/test/ui/issues/issue-48728.stderr
index 628f026b680..0bb46724f61 100644
--- a/src/test/ui/issues/issue-48728.stderr
+++ b/src/test/ui/issues/issue-48728.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `std::clone::Clone` for type `Node<[_]>`
+error[E0119]: conflicting implementations of trait `Clone` for type `Node<[_]>`
   --> $DIR/issue-48728.rs:4:10
    |
 LL | #[derive(Clone)]
diff --git a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs
index 6851b67cb3b..e98affc5cc2 100644
--- a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs
+++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.rs
@@ -19,5 +19,5 @@ impl TraitB for B { //~ ERROR not all trait items implemented, missing: `MyA`
 
 fn main() {
     let _ = [0; B::VALUE];
-    //~^ ERROR evaluation of constant value failed
+    //~^ constant
 }
diff --git a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr
index 2c2cd5c5244..ba385d887fb 100644
--- a/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr
+++ b/src/test/ui/issues/issue-69602-type-err-during-codegen-ice.stderr
@@ -13,13 +13,13 @@ LL |     type MyA: TraitA;
 LL | impl TraitB for B {
    | ^^^^^^^^^^^^^^^^^ missing `MyA` in implementation
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-69602-type-err-during-codegen-ice.rs:21:17
    |
 LL |     let _ = [0; B::VALUE];
-   |                 ^^^^^^^^ referenced constant has errors
+   |                 ^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0046, E0080, E0437.
+Some errors have detailed explanations: E0046, E0437.
 For more information about an error, try `rustc --explain E0046`.
diff --git a/src/test/ui/issues/issue-7970a.stderr b/src/test/ui/issues/issue-7970a.stderr
index ea400d7e191..b04a0eef371 100644
--- a/src/test/ui/issues/issue-7970a.stderr
+++ b/src/test/ui/issues/issue-7970a.stderr
@@ -6,6 +6,12 @@ LL | macro_rules! one_arg_macro {
 ...
 LL |     one_arg_macro!();
    |     ^^^^^^^^^^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$fmt:expr`
+  --> $DIR/issue-7970a.rs:2:6
+   |
+LL |     ($fmt:expr) => (print!(concat!($fmt, "\n")));
+   |      ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lang-items/lang-item-generic-requirements.rs b/src/test/ui/lang-items/lang-item-generic-requirements.rs
index fbb56e528c0..3d33adf6831 100644
--- a/src/test/ui/lang-items/lang-item-generic-requirements.rs
+++ b/src/test/ui/lang-items/lang-item-generic-requirements.rs
@@ -22,8 +22,6 @@ trait MyIndex<'a, T> {}
 #[lang = "phantom_data"]
 //~^ ERROR `phantom_data` language item must be applied to a struct with 1 generic argument
 struct MyPhantomData<T, U>;
-//~^ ERROR parameter `T` is never used
-//~| ERROR parameter `U` is never used
 
 #[lang = "owned_box"]
 //~^ ERROR `owned_box` language item must be applied to a struct with at least 1 generic argument
diff --git a/src/test/ui/lang-items/lang-item-generic-requirements.stderr b/src/test/ui/lang-items/lang-item-generic-requirements.stderr
index 326f5b0d595..4d349a25f9c 100644
--- a/src/test/ui/lang-items/lang-item-generic-requirements.stderr
+++ b/src/test/ui/lang-items/lang-item-generic-requirements.stderr
@@ -33,7 +33,7 @@ LL | struct MyPhantomData<T, U>;
    |                     ------ this struct has 2 generic arguments
 
 error[E0718]: `owned_box` language item must be applied to a struct with at least 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:28:1
+  --> $DIR/lang-item-generic-requirements.rs:26:1
    |
 LL | #[lang = "owned_box"]
    | ^^^^^^^^^^^^^^^^^^^^^
@@ -42,7 +42,7 @@ LL | struct Foo;
    |           - this struct has 0 generic arguments
 
 error[E0718]: `start` language item must be applied to a function with 1 generic argument
-  --> $DIR/lang-item-generic-requirements.rs:34:1
+  --> $DIR/lang-item-generic-requirements.rs:32:1
    |
 LL | #[lang = "start"]
    | ^^^^^^^^^^^^^^^^^
@@ -50,25 +50,6 @@ LL |
 LL | fn start(_: *const u8, _: isize, _: *const *const u8) -> isize {
    |         - this function has 0 generic arguments
 
-error[E0392]: parameter `T` is never used
-  --> $DIR/lang-item-generic-requirements.rs:24:22
-   |
-LL | struct MyPhantomData<T, U>;
-   |                      ^ unused parameter
-   |
-   = help: consider removing `T` or referring to it in a field
-   = help: if you intended `T` to be a const parameter, use `const T: usize` instead
-
-error[E0392]: parameter `U` is never used
-  --> $DIR/lang-item-generic-requirements.rs:24:25
-   |
-LL | struct MyPhantomData<T, U>;
-   |                         ^ unused parameter
-   |
-   = help: consider removing `U` or referring to it in a field
-   = help: if you intended `U` to be a const parameter, use `const U: usize` instead
-
-error: aborting due to 8 previous errors
+error: aborting due to 6 previous errors
 
-Some errors have detailed explanations: E0392, E0718.
-For more information about an error, try `rustc --explain E0392`.
+For more information about this error, try `rustc --explain E0718`.
diff --git a/src/test/ui/lexer/error-stage.rs b/src/test/ui/lexer/error-stage.rs
new file mode 100644
index 00000000000..c8d88f745a1
--- /dev/null
+++ b/src/test/ui/lexer/error-stage.rs
@@ -0,0 +1,80 @@
+// This test is about the treatment of invalid literals. In particular, some
+// literals are only considered invalid if they survive to HIR lowering.
+//
+// Literals with bad suffixes
+// --------------------------
+// Literals consist of a primary part and an optional suffix.
+// https://doc.rust-lang.org/reference/tokens.html#suffixes says:
+//
+//   Any kind of literal (string, integer, etc) with any suffix is valid as a
+//   token, and can be passed to a macro without producing an error. The macro
+//   itself will decide how to interpret such a token and whether to produce an
+//   error or not.
+//
+//   ```
+//   macro_rules! blackhole { ($tt:tt) => () }
+//   blackhole!("string"suffix); // OK
+//   ```
+//
+//   However, suffixes on literal tokens parsed as Rust code are restricted.
+//   Any suffixes are rejected on non-numeric literal tokens, and numeric
+//   literal tokens are accepted only with suffixes from the list below.
+//
+//   Integer: u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize
+//   Floating-point: f32, f64
+//
+// This means that something like `"string"any_suffix` is a token accepted by
+// the lexer, but rejected later for being an invalid combination of primary
+// part and suffix.
+//
+// `0b10f32` is a similar case. `0b10` is a valid primary part that is a valid
+// *integer* literal when no suffix is present. It only causes an error later
+// when combined with the `f32` float suffix.
+//
+// However, `0b10.0f32` is different. It is rejected by the lexer because
+// `0b10.0` is not a valid token even on its own.
+//
+// This difference is unfortunate, but it's baked into the language now.
+//
+// Too-large integer literals
+// --------------------------
+// https://doc.rust-lang.org/reference/tokens.html#integer-literals says that
+// literals like `128_i8` and `256_u8` "are too big for their type, but are
+// still valid tokens".
+
+macro_rules! sink {
+    ($($x:tt;)*) => {()}
+}
+
+// The invalid literals are ignored because the macro consumes them. Except for
+// `0b10.0f32` because it's a lexer error.
+const _: () = sink! {
+    "string"any_suffix; // OK
+    10u123; // OK
+    10.0f123; // OK
+    0b10f32; // OK
+    0b10.0f32; //~ ERROR binary float literal is not supported
+    999340282366920938463463374607431768211455999; // OK
+};
+
+// The invalid literals used to cause errors, but this was changed by #102944.
+// Except for `0b010.0f32`, because it's a lexer error.
+#[cfg(FALSE)]
+fn configured_out() {
+    "string"any_suffix; // OK
+    10u123; // OK
+    10.0f123; // OK
+    0b10f32; // OK
+    0b10.0f32; //~ ERROR binary float literal is not supported
+    999340282366920938463463374607431768211455999; // OK
+}
+
+// All the invalid literals cause errors.
+fn main() {
+    "string"any_suffix; //~ ERROR suffixes on string literals are invalid
+    10u123; //~ ERROR invalid width `123` for integer literal
+    10.0f123; //~ ERROR invalid width `123` for float literal
+    0b10f32; //~ ERROR binary float literal is not supported
+    0b10.0f32; //~ ERROR binary float literal is not supported
+    999340282366920938463463374607431768211455999; //~ ERROR integer literal is too large
+}
diff --git a/src/test/ui/lexer/error-stage.stderr b/src/test/ui/lexer/error-stage.stderr
new file mode 100644
index 00000000000..697a7c28da1
--- /dev/null
+++ b/src/test/ui/lexer/error-stage.stderr
@@ -0,0 +1,54 @@
+error: binary float literal is not supported
+  --> $DIR/error-stage.rs:56:5
+   |
+LL |     0b10.0f32;
+   |     ^^^^^^
+
+error: binary float literal is not supported
+  --> $DIR/error-stage.rs:68:5
+   |
+LL |     0b10.0f32;
+   |     ^^^^^^
+
+error: binary float literal is not supported
+  --> $DIR/error-stage.rs:78:5
+   |
+LL |     0b10.0f32;
+   |     ^^^^^^
+
+error: suffixes on string literals are invalid
+  --> $DIR/error-stage.rs:74:5
+   |
+LL |     "string"any_suffix;
+   |     ^^^^^^^^^^^^^^^^^^ invalid suffix `any_suffix`
+
+error: invalid width `123` for integer literal
+  --> $DIR/error-stage.rs:75:5
+   |
+LL |     10u123;
+   |     ^^^^^^
+   |
+   = help: valid widths are 8, 16, 32, 64 and 128
+
+error: invalid width `123` for float literal
+  --> $DIR/error-stage.rs:76:5
+   |
+LL |     10.0f123;
+   |     ^^^^^^^^
+   |
+   = help: valid widths are 32 and 64
+
+error: binary float literal is not supported
+  --> $DIR/error-stage.rs:77:5
+   |
+LL |     0b10f32;
+   |     ^^^^^^^ not supported
+
+error: integer literal is too large
+  --> $DIR/error-stage.rs:79:5
+   |
+LL |     999340282366920938463463374607431768211455999;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/test/ui/limits/issue-55878.stderr b/src/test/ui/limits/issue-55878.stderr
index ee6aab748e4..e35f9f14c7e 100644
--- a/src/test/ui/limits/issue-55878.stderr
+++ b/src/test/ui/limits/issue-55878.stderr
@@ -9,14 +9,30 @@ LL |     intrinsics::size_of::<T>()
 LL |     println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
    |                          ---------------------------------------------- inside `main` at $DIR/issue-55878.rs:7:26
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/issue-55878.rs:7:26
    |
 LL |     println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 2 previous errors
+note: erroneous constant used
+  --> $DIR/issue-55878.rs:7:26
+   |
+LL |     println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: erroneous constant used
+  --> $DIR/issue-55878.rs:7:26
+   |
+LL |     println!("Size: {}", std::mem::size_of::<[u8; u64::MAX as usize]>());
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/lint/lint-incoherent-auto-trait-objects.stderr b/src/test/ui/lint/lint-incoherent-auto-trait-objects.stderr
index 2cc4d382d9d..553ab3869b3 100644
--- a/src/test/ui/lint/lint-incoherent-auto-trait-objects.stderr
+++ b/src/test/ui/lint/lint-incoherent-auto-trait-objects.stderr
@@ -1,36 +1,36 @@
-error: conflicting implementations of trait `Foo` for type `(dyn std::marker::Send + 'static)`: (E0119)
+error: conflicting implementations of trait `Foo` for type `(dyn Send + 'static)`: (E0119)
   --> $DIR/lint-incoherent-auto-trait-objects.rs:5:1
    |
 LL | impl Foo for dyn Send {}
    | --------------------- first implementation here
 LL |
 LL | impl Foo for dyn Send + Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
    = note: `#[deny(order_dependent_trait_objects)]` on by default
 
-error: conflicting implementations of trait `Foo` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/lint-incoherent-auto-trait-objects.rs:11:1
    |
 LL | impl Foo for dyn Send + Sync {}
    | ---------------------------- first implementation here
 LL |
 LL | impl Foo for dyn Sync + Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
 
-error: conflicting implementations of trait `Foo` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/lint-incoherent-auto-trait-objects.rs:15:1
    |
 LL | impl Foo for dyn Sync + Send {}
    | ---------------------------- first implementation here
 ...
 LL | impl Foo for dyn Send + Sync + Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
@@ -38,42 +38,42 @@ LL | impl Foo for dyn Send + Sync + Send {}
 error: aborting due to 3 previous errors
 
 Future incompatibility report: Future breakage diagnostic:
-error: conflicting implementations of trait `Foo` for type `(dyn std::marker::Send + 'static)`: (E0119)
+error: conflicting implementations of trait `Foo` for type `(dyn Send + 'static)`: (E0119)
   --> $DIR/lint-incoherent-auto-trait-objects.rs:5:1
    |
 LL | impl Foo for dyn Send {}
    | --------------------- first implementation here
 LL |
 LL | impl Foo for dyn Send + Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
    = note: `#[deny(order_dependent_trait_objects)]` on by default
 
 Future breakage diagnostic:
-error: conflicting implementations of trait `Foo` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/lint-incoherent-auto-trait-objects.rs:11:1
    |
 LL | impl Foo for dyn Send + Sync {}
    | ---------------------------- first implementation here
 LL |
 LL | impl Foo for dyn Sync + Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
    = note: `#[deny(order_dependent_trait_objects)]` on by default
 
 Future breakage diagnostic:
-error: conflicting implementations of trait `Foo` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+error: conflicting implementations of trait `Foo` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/lint-incoherent-auto-trait-objects.rs:15:1
    |
 LL | impl Foo for dyn Sync + Send {}
    | ---------------------------- first implementation here
 ...
 LL | impl Foo for dyn Send + Sync + Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
diff --git a/src/test/ui/lint/opaque-ty-ffi-normalization-cycle.rs b/src/test/ui/lint/opaque-ty-ffi-normalization-cycle.rs
new file mode 100644
index 00000000000..c83bca4a4c5
--- /dev/null
+++ b/src/test/ui/lint/opaque-ty-ffi-normalization-cycle.rs
@@ -0,0 +1,41 @@
+#![feature(type_alias_impl_trait)]
+#![allow(unused)]
+#![deny(improper_ctypes)]
+
+pub trait TraitA {
+    type Assoc;
+}
+
+impl TraitA for u32 {
+    type Assoc = u32;
+}
+
+pub trait TraitB {
+    type Assoc;
+}
+
+impl<T> TraitB for T
+where
+    T: TraitA,
+{
+    type Assoc = <T as TraitA>::Assoc;
+}
+
+type AliasA = impl TraitA<Assoc = u32>;
+
+type AliasB = impl TraitB;
+
+fn use_of_a() -> AliasA {
+    3
+}
+
+fn use_of_b() -> AliasB {
+    3
+}
+
+extern "C" {
+    fn lint_me() -> <AliasB as TraitB>::Assoc;
+    //~^ ERROR `extern` block uses type `AliasB`, which is not FFI-safe
+}
+
+fn main() {}
diff --git a/src/test/ui/lint/opaque-ty-ffi-normalization-cycle.stderr b/src/test/ui/lint/opaque-ty-ffi-normalization-cycle.stderr
new file mode 100644
index 00000000000..e8d696477ad
--- /dev/null
+++ b/src/test/ui/lint/opaque-ty-ffi-normalization-cycle.stderr
@@ -0,0 +1,15 @@
+error: `extern` block uses type `AliasB`, which is not FFI-safe
+  --> $DIR/opaque-ty-ffi-normalization-cycle.rs:37:21
+   |
+LL |     fn lint_me() -> <AliasB as TraitB>::Assoc;
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: opaque types have no C equivalent
+note: the lint level is defined here
+  --> $DIR/opaque-ty-ffi-normalization-cycle.rs:3:9
+   |
+LL | #![deny(improper_ctypes)]
+   |         ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr b/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr
index 09dd16a0b0d..1e73320e439 100644
--- a/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr
+++ b/src/test/ui/macros/assert-trailing-junk.with-generic-asset.stderr
@@ -17,6 +17,8 @@ LL |     assert!(true, "whatever" blah);
    |                             -^^^^ no rules expected this token in macro call
    |                             |
    |                             help: missing comma here
+   |
+   = note: while trying to match sequence start
 
 error: unexpected string literal
   --> $DIR/assert-trailing-junk.rs:18:18
@@ -33,6 +35,8 @@ LL |     assert!(true "whatever" blah);
    |                            -^^^^ no rules expected this token in macro call
    |                            |
    |                            help: missing comma here
+   |
+   = note: while trying to match sequence start
 
 error: macro requires an expression as an argument
   --> $DIR/assert-trailing-junk.rs:22:5
diff --git a/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr b/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr
index 09dd16a0b0d..1e73320e439 100644
--- a/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr
+++ b/src/test/ui/macros/assert-trailing-junk.without-generic-asset.stderr
@@ -17,6 +17,8 @@ LL |     assert!(true, "whatever" blah);
    |                             -^^^^ no rules expected this token in macro call
    |                             |
    |                             help: missing comma here
+   |
+   = note: while trying to match sequence start
 
 error: unexpected string literal
   --> $DIR/assert-trailing-junk.rs:18:18
@@ -33,6 +35,8 @@ LL |     assert!(true "whatever" blah);
    |                            -^^^^ no rules expected this token in macro call
    |                            |
    |                            help: missing comma here
+   |
+   = note: while trying to match sequence start
 
 error: macro requires an expression as an argument
   --> $DIR/assert-trailing-junk.rs:22:5
diff --git a/src/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs
index aa8f578adf6..fb40cd5387b 100644
--- a/src/test/ui/macros/issue-68060.rs
+++ b/src/test/ui/macros/issue-68060.rs
@@ -3,7 +3,11 @@ fn main() {
         .map(
             #[target_feature(enable = "")]
             //~^ ERROR: attribute should be applied to a function
+            //~| ERROR: feature named `` is not valid
+            //~| NOTE: `` is not valid for this target
             #[track_caller]
+            //~^ ERROR: `#[track_caller]` on closures is currently unstable
+            //~| NOTE: see issue #87417
             |_| (),
             //~^ NOTE: not a function
         )
diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr
index b13e418e664..52e6ed92e9d 100644
--- a/src/test/ui/macros/issue-68060.stderr
+++ b/src/test/ui/macros/issue-68060.stderr
@@ -7,5 +7,21 @@ LL |             #[target_feature(enable = "")]
 LL |             |_| (),
    |             ------ not a function definition
 
-error: aborting due to previous error
+error: the feature named `` is not valid for this target
+  --> $DIR/issue-68060.rs:4:30
+   |
+LL |             #[target_feature(enable = "")]
+   |                              ^^^^^^^^^^^ `` is not valid for this target
+
+error[E0658]: `#[track_caller]` on closures is currently unstable
+  --> $DIR/issue-68060.rs:8:13
+   |
+LL |             #[track_caller]
+   |             ^^^^^^^^^^^^^^^
+   |
+   = note: see issue #87417 <https://github.com/rust-lang/rust/issues/87417> for more information
+   = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable
+
+error: aborting due to 3 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/macros/macro-at-most-once-rep-2015.stderr b/src/test/ui/macros/macro-at-most-once-rep-2015.stderr
index 9a3df858e51..7c45b85bc8d 100644
--- a/src/test/ui/macros/macro-at-most-once-rep-2015.stderr
+++ b/src/test/ui/macros/macro-at-most-once-rep-2015.stderr
@@ -12,6 +12,8 @@ LL | macro_rules! foo {
 ...
 LL |     foo!(a?);
    |           ^ no rules expected this token in macro call
+   |
+   = note: while trying to match sequence end
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2015.rs:26:11
@@ -21,6 +23,8 @@ LL | macro_rules! foo {
 ...
 LL |     foo!(a?a);
    |           ^ no rules expected this token in macro call
+   |
+   = note: while trying to match sequence end
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2015.rs:27:11
@@ -30,6 +34,8 @@ LL | macro_rules! foo {
 ...
 LL |     foo!(a?a?a);
    |           ^ no rules expected this token in macro call
+   |
+   = note: while trying to match sequence end
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2015.rs:29:5
@@ -39,6 +45,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!();
    |     ^^^^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2015.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2015.rs:30:15
@@ -48,6 +60,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!(a);
    |               ^ missing tokens in macro arguments
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2015.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2015.rs:31:15
@@ -57,6 +75,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!(a?);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2015.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2015.rs:32:15
@@ -66,6 +90,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!(a?a);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2015.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2015.rs:36:5
@@ -75,6 +105,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!();
    |     ^^^^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2015.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2015.rs:37:15
@@ -84,6 +120,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!(a);
    |               ^ missing tokens in macro arguments
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2015.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2015.rs:38:15
@@ -93,6 +135,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!(a?);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2015.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2015.rs:39:15
@@ -102,6 +150,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!(a?a);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2015.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/macros/macro-at-most-once-rep-2018.stderr b/src/test/ui/macros/macro-at-most-once-rep-2018.stderr
index 013fabe13e5..696520b2826 100644
--- a/src/test/ui/macros/macro-at-most-once-rep-2018.stderr
+++ b/src/test/ui/macros/macro-at-most-once-rep-2018.stderr
@@ -12,6 +12,8 @@ LL | macro_rules! foo {
 ...
 LL |     foo!(a?);
    |           ^ no rules expected this token in macro call
+   |
+   = note: while trying to match sequence end
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2018.rs:26:11
@@ -21,6 +23,8 @@ LL | macro_rules! foo {
 ...
 LL |     foo!(a?a);
    |           ^ no rules expected this token in macro call
+   |
+   = note: while trying to match sequence end
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2018.rs:27:11
@@ -30,6 +34,8 @@ LL | macro_rules! foo {
 ...
 LL |     foo!(a?a?a);
    |           ^ no rules expected this token in macro call
+   |
+   = note: while trying to match sequence end
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2018.rs:29:5
@@ -39,6 +45,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!();
    |     ^^^^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2018.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2018.rs:30:15
@@ -48,6 +60,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!(a);
    |               ^ missing tokens in macro arguments
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2018.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2018.rs:31:15
@@ -57,6 +75,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!(a?);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2018.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2018.rs:32:15
@@ -66,6 +90,12 @@ LL | macro_rules! barplus {
 ...
 LL |     barplus!(a?a);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `+`
+  --> $DIR/macro-at-most-once-rep-2018.rs:15:11
+   |
+LL |     ($(a)?+) => {}; // ok. matches "a+" and "+"
+   |           ^
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2018.rs:36:5
@@ -75,6 +105,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!();
    |     ^^^^^^^^^^ missing tokens in macro arguments
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2018.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: unexpected end of macro invocation
   --> $DIR/macro-at-most-once-rep-2018.rs:37:15
@@ -84,6 +120,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!(a);
    |               ^ missing tokens in macro arguments
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2018.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2018.rs:38:15
@@ -93,6 +135,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!(a?);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2018.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: no rules expected the token `?`
   --> $DIR/macro-at-most-once-rep-2018.rs:39:15
@@ -102,6 +150,12 @@ LL | macro_rules! barstar {
 ...
 LL |     barstar!(a?a);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match `*`
+  --> $DIR/macro-at-most-once-rep-2018.rs:19:11
+   |
+LL |     ($(a)?*) => {}; // ok. matches "a*" and "*"
+   |           ^
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/macros/macro-non-lifetime.stderr b/src/test/ui/macros/macro-non-lifetime.stderr
index 6234735dfc8..e1ed87f9435 100644
--- a/src/test/ui/macros/macro-non-lifetime.stderr
+++ b/src/test/ui/macros/macro-non-lifetime.stderr
@@ -6,6 +6,12 @@ LL | macro_rules! m { ($x:lifetime) => { } }
 ...
 LL |     m!(a);
    |        ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$x:lifetime`
+  --> $DIR/macro-non-lifetime.rs:3:19
+   |
+LL | macro_rules! m { ($x:lifetime) => { } }
+   |                   ^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/macros/missing-comma.stderr b/src/test/ui/macros/missing-comma.stderr
index 6da92bdea19..81877a29ed8 100644
--- a/src/test/ui/macros/missing-comma.stderr
+++ b/src/test/ui/macros/missing-comma.stderr
@@ -14,6 +14,12 @@ LL |     foo!(a b);
    |           -^ no rules expected this token in macro call
    |           |
    |           help: missing comma here
+   |
+note: while trying to match meta-variable `$a:ident`
+  --> $DIR/missing-comma.rs:2:6
+   |
+LL |     ($a:ident) => ();
+   |      ^^^^^^^^
 
 error: no rules expected the token `e`
   --> $DIR/missing-comma.rs:23:21
@@ -25,6 +31,12 @@ LL |     foo!(a, b, c, d e);
    |                    -^ no rules expected this token in macro call
    |                    |
    |                    help: missing comma here
+   |
+note: while trying to match meta-variable `$d:ident`
+  --> $DIR/missing-comma.rs:5:36
+   |
+LL |     ($a:ident, $b:ident, $c:ident, $d:ident) => ();
+   |                                    ^^^^^^^^
 
 error: no rules expected the token `d`
   --> $DIR/missing-comma.rs:25:18
@@ -36,6 +48,12 @@ LL |     foo!(a, b, c d, e);
    |                 -^ no rules expected this token in macro call
    |                 |
    |                 help: missing comma here
+   |
+note: while trying to match meta-variable `$c:ident`
+  --> $DIR/missing-comma.rs:4:26
+   |
+LL |     ($a:ident, $b:ident, $c:ident) => ();
+   |                          ^^^^^^^^
 
 error: no rules expected the token `d`
   --> $DIR/missing-comma.rs:27:18
@@ -45,6 +63,12 @@ LL | macro_rules! foo {
 ...
 LL |     foo!(a, b, c d e);
    |                  ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$c:ident`
+  --> $DIR/missing-comma.rs:4:26
+   |
+LL |     ($a:ident, $b:ident, $c:ident) => ();
+   |                          ^^^^^^^^
 
 error: unexpected end of macro invocation
   --> $DIR/missing-comma.rs:29:23
@@ -54,6 +78,12 @@ LL | macro_rules! bar {
 ...
 LL |     bar!(Level::Error, );
    |                       ^ missing tokens in macro arguments
+   |
+note: while trying to match meta-variable `$arg:tt`
+  --> $DIR/missing-comma.rs:10:19
+   |
+LL |     ($lvl:expr, $($arg:tt)+) => {}
+   |                   ^^^^^^^
 
 error: no rules expected the token `,`
   --> $DIR/missing-comma.rs:32:38
@@ -63,6 +93,12 @@ LL | macro_rules! check {
 ...
 LL |     check!(<str as Debug>::fmt, "fmt",);
    |                                      ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$expected:expr`
+  --> $DIR/missing-comma.rs:14:14
+   |
+LL |     ($ty:ty, $expected:expr) => {};
+   |              ^^^^^^^^^^^^^^
 
 error: aborting due to 7 previous errors
 
diff --git a/src/test/ui/macros/nonterminal-matching.stderr b/src/test/ui/macros/nonterminal-matching.stderr
index 585f2355321..5bbd5439098 100644
--- a/src/test/ui/macros/nonterminal-matching.stderr
+++ b/src/test/ui/macros/nonterminal-matching.stderr
@@ -10,6 +10,14 @@ LL |     n!(a $nt_item b);
 LL | complex_nonterminal!(enum E {});
    | ------------------------------- in this macro invocation
    |
+note: while trying to match `enum E {}`
+  --> $DIR/nonterminal-matching.rs:15:15
+   |
+LL |     macro n(a $nt_item b) {
+   |               ^^^^^^^^
+...
+LL | complex_nonterminal!(enum E {});
+   | ------------------------------- in this macro invocation
    = 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/src/test/ui/macros/recovery-allowed.rs b/src/test/ui/macros/recovery-allowed.rs
new file mode 100644
index 00000000000..ebf65f1cc01
--- /dev/null
+++ b/src/test/ui/macros/recovery-allowed.rs
@@ -0,0 +1,8 @@
+macro_rules! please_recover {
+    ($a:expr) => {};
+}
+
+please_recover! { not 1 }
+//~^ ERROR unexpected `1` after identifier
+
+fn main() {}
diff --git a/src/test/ui/macros/recovery-allowed.stderr b/src/test/ui/macros/recovery-allowed.stderr
new file mode 100644
index 00000000000..ec036e8b1e2
--- /dev/null
+++ b/src/test/ui/macros/recovery-allowed.stderr
@@ -0,0 +1,10 @@
+error: unexpected `1` after identifier
+  --> $DIR/recovery-allowed.rs:5:23
+   |
+LL | please_recover! { not 1 }
+   |                   ----^
+   |                   |
+   |                   help: use `!` to perform bitwise not
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/macros/recovery-forbidden.rs b/src/test/ui/macros/recovery-forbidden.rs
new file mode 100644
index 00000000000..5dd2619330c
--- /dev/null
+++ b/src/test/ui/macros/recovery-forbidden.rs
@@ -0,0 +1,13 @@
+// check-pass
+
+macro_rules! dont_recover_here {
+    ($e:expr) => {
+        compile_error!("Must not recover to single !1 expr");
+    };
+
+    (not $a:literal) => {};
+}
+
+dont_recover_here! { not 1 }
+
+fn main() {}
diff --git a/src/test/ui/macros/trace_faulty_macros.stderr b/src/test/ui/macros/trace_faulty_macros.stderr
index d6fc6940214..21e47da0757 100644
--- a/src/test/ui/macros/trace_faulty_macros.stderr
+++ b/src/test/ui/macros/trace_faulty_macros.stderr
@@ -10,6 +10,7 @@ LL |         my_faulty_macro!(bcd);
 LL |     my_faulty_macro!();
    |     ------------------ in this macro invocation
    |
+   = note: while trying to match end of macro
    = note: this error originates in the macro `my_faulty_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: trace_macro
diff --git a/src/test/ui/never_type/exhaustive_patterns.rs b/src/test/ui/never_type/exhaustive_patterns.rs
new file mode 100644
index 00000000000..2e23fa18280
--- /dev/null
+++ b/src/test/ui/never_type/exhaustive_patterns.rs
@@ -0,0 +1,21 @@
+// check-fail
+// known-bug: #104034
+
+#![feature(exhaustive_patterns, never_type)]
+
+mod inner {
+    pub struct Wrapper<T>(T);
+}
+
+enum Either<A, B> {
+    A(A),
+    B(inner::Wrapper<B>),
+}
+
+fn foo() -> Either<(), !> {
+    Either::A(())
+}
+
+fn main() {
+    let Either::A(()) = foo();
+}
diff --git a/src/test/ui/never_type/exhaustive_patterns.stderr b/src/test/ui/never_type/exhaustive_patterns.stderr
new file mode 100644
index 00000000000..e41baf86218
--- /dev/null
+++ b/src/test/ui/never_type/exhaustive_patterns.stderr
@@ -0,0 +1,25 @@
+error[E0005]: refutable pattern in local binding: `Either::B(_)` not covered
+  --> $DIR/exhaustive_patterns.rs:20:9
+   |
+LL |     let Either::A(()) = foo();
+   |         ^^^^^^^^^^^^^ pattern `Either::B(_)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+note: `Either<(), !>` defined here
+  --> $DIR/exhaustive_patterns.rs:12:5
+   |
+LL | enum Either<A, B> {
+   |      ------
+LL |     A(A),
+LL |     B(inner::Wrapper<B>),
+   |     ^ not covered
+   = note: the matched value is of type `Either<(), !>`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |     if let Either::A(()) = foo() { todo!() }
+   |     ++                           ~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/src/test/ui/nll/user-annotations/ascribed-type-wf.rs b/src/test/ui/nll/user-annotations/ascribed-type-wf.rs
new file mode 100644
index 00000000000..14460dea5b5
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/ascribed-type-wf.rs
@@ -0,0 +1,16 @@
+// check-pass
+// known-bug: #101350
+
+trait Trait {
+    type Ty;
+}
+
+impl Trait for &'static () {
+    type Ty = ();
+}
+
+fn extend<'a>() {
+    None::<<&'a () as Trait>::Ty>;
+}
+
+fn main() {}
diff --git a/src/test/ui/on-unimplemented/issue-104140.rs b/src/test/ui/on-unimplemented/issue-104140.rs
new file mode 100644
index 00000000000..ade3f727004
--- /dev/null
+++ b/src/test/ui/on-unimplemented/issue-104140.rs
@@ -0,0 +1,8 @@
+#![feature(rustc_attrs)]
+
+trait Foo {}
+
+#[rustc_on_unimplemented] //~ ERROR malformed `rustc_on_unimplemented` attribute input
+impl Foo for u32 {}
+
+fn main() {}
diff --git a/src/test/ui/on-unimplemented/issue-104140.stderr b/src/test/ui/on-unimplemented/issue-104140.stderr
new file mode 100644
index 00000000000..ddb1f50f0bb
--- /dev/null
+++ b/src/test/ui/on-unimplemented/issue-104140.stderr
@@ -0,0 +1,15 @@
+error: malformed `rustc_on_unimplemented` attribute input
+  --> $DIR/issue-104140.rs:5:1
+   |
+LL | #[rustc_on_unimplemented]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[rustc_on_unimplemented = "message"]
+   | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")]
+   | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr
index 001c68a9774..acc2099bbc6 100644
--- a/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr
+++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail-2018.stderr
@@ -6,6 +6,12 @@ LL | macro_rules! accept_pat {
 ...
 LL | accept_pat!(p | q);
    |               ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$p:pat`
+  --> $DIR/or-patterns-syntactic-fail-2018.rs:9:6
+   |
+LL |     ($p:pat) => {};
+   |      ^^^^^^
 
 error: no rules expected the token `|`
   --> $DIR/or-patterns-syntactic-fail-2018.rs:13:13
@@ -15,6 +21,12 @@ LL | macro_rules! accept_pat {
 ...
 LL | accept_pat!(|p| q);
    |             ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$p:pat`
+  --> $DIR/or-patterns-syntactic-fail-2018.rs:9:6
+   |
+LL |     ($p:pat) => {};
+   |      ^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/panic-handler/panic-handler-std.stderr b/src/test/ui/panic-handler/panic-handler-std.stderr
index e4069b196ff..7c7feffe76a 100644
--- a/src/test/ui/panic-handler/panic-handler-std.stderr
+++ b/src/test/ui/panic-handler/panic-handler-std.stderr
@@ -8,12 +8,6 @@ LL | fn panic(info: PanicInfo) -> ! {
    = note: first definition in `std` loaded from SYSROOT/libstd-*.rlib
    = note: second definition in the local crate (`panic_handler_std`)
 
-error: argument should be `&PanicInfo`
-  --> $DIR/panic-handler-std.rs:8:16
-   |
-LL | fn panic(info: PanicInfo) -> ! {
-   |                ^^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0152`.
diff --git a/src/test/ui/parser/bad-lit-suffixes.rs b/src/test/ui/parser/bad-lit-suffixes.rs
index 9724533c420..8cb9ef7e0c9 100644
--- a/src/test/ui/parser/bad-lit-suffixes.rs
+++ b/src/test/ui/parser/bad-lit-suffixes.rs
@@ -1,3 +1,5 @@
+#![feature(rustc_attrs)]
+
 extern
     "C"suffix //~ ERROR suffixes on string literals are invalid
     fn foo() {}
@@ -24,3 +26,19 @@ fn main() {
     1.0suffix; //~ ERROR invalid suffix `suffix` for float literal
     1.0e10suffix; //~ ERROR invalid suffix `suffix` for float literal
 }
+
+#[rustc_dummy = "string"suffix]
+//~^ ERROR unexpected expression: `"string"suffix`
+fn f() {}
+
+#[must_use = "string"suffix]
+//~^ ERROR unexpected expression: `"string"suffix`
+fn g() {}
+
+#[link(name = "string"suffix)]
+//~^ ERROR suffixes on string literals are invalid
+extern "C" {}
+
+#[rustc_layout_scalar_valid_range_start(0suffix)]
+//~^ ERROR invalid suffix `suffix` for number literal
+struct S;
diff --git a/src/test/ui/parser/bad-lit-suffixes.stderr b/src/test/ui/parser/bad-lit-suffixes.stderr
index f74eef32445..756f99ab12c 100644
--- a/src/test/ui/parser/bad-lit-suffixes.stderr
+++ b/src/test/ui/parser/bad-lit-suffixes.stderr
@@ -1,53 +1,79 @@
 error: suffixes on string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:2:5
+  --> $DIR/bad-lit-suffixes.rs:4:5
    |
 LL |     "C"suffix
    |     ^^^^^^^^^ invalid suffix `suffix`
 
 error: suffixes on string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:6:5
+  --> $DIR/bad-lit-suffixes.rs:8:5
    |
 LL |     "C"suffix
    |     ^^^^^^^^^ invalid suffix `suffix`
 
+error: unexpected expression: `"string"suffix`
+  --> $DIR/bad-lit-suffixes.rs:30:17
+   |
+LL | #[rustc_dummy = "string"suffix]
+   |                 ^^^^^^^^^^^^^^
+
+error: unexpected expression: `"string"suffix`
+  --> $DIR/bad-lit-suffixes.rs:34:14
+   |
+LL | #[must_use = "string"suffix]
+   |              ^^^^^^^^^^^^^^
+
+error: suffixes on string literals are invalid
+  --> $DIR/bad-lit-suffixes.rs:38:15
+   |
+LL | #[link(name = "string"suffix)]
+   |               ^^^^^^^^^^^^^^ invalid suffix `suffix`
+
+error: invalid suffix `suffix` for number literal
+  --> $DIR/bad-lit-suffixes.rs:42:41
+   |
+LL | #[rustc_layout_scalar_valid_range_start(0suffix)]
+   |                                         ^^^^^^^ invalid suffix `suffix`
+   |
+   = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
+
 error: suffixes on string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:10:5
+  --> $DIR/bad-lit-suffixes.rs:12:5
    |
 LL |     ""suffix;
    |     ^^^^^^^^ invalid suffix `suffix`
 
 error: suffixes on byte string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:11:5
+  --> $DIR/bad-lit-suffixes.rs:13:5
    |
 LL |     b""suffix;
    |     ^^^^^^^^^ invalid suffix `suffix`
 
 error: suffixes on string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:12:5
+  --> $DIR/bad-lit-suffixes.rs:14:5
    |
 LL |     r#""#suffix;
    |     ^^^^^^^^^^^ invalid suffix `suffix`
 
 error: suffixes on byte string literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:13:5
+  --> $DIR/bad-lit-suffixes.rs:15:5
    |
 LL |     br#""#suffix;
    |     ^^^^^^^^^^^^ invalid suffix `suffix`
 
 error: suffixes on char literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:14:5
+  --> $DIR/bad-lit-suffixes.rs:16:5
    |
 LL |     'a'suffix;
    |     ^^^^^^^^^ invalid suffix `suffix`
 
 error: suffixes on byte literals are invalid
-  --> $DIR/bad-lit-suffixes.rs:15:5
+  --> $DIR/bad-lit-suffixes.rs:17:5
    |
 LL |     b'a'suffix;
    |     ^^^^^^^^^^ invalid suffix `suffix`
 
 error: invalid width `1024` for integer literal
-  --> $DIR/bad-lit-suffixes.rs:17:5
+  --> $DIR/bad-lit-suffixes.rs:19:5
    |
 LL |     1234u1024;
    |     ^^^^^^^^^
@@ -55,7 +81,7 @@ LL |     1234u1024;
    = help: valid widths are 8, 16, 32, 64 and 128
 
 error: invalid width `1024` for integer literal
-  --> $DIR/bad-lit-suffixes.rs:18:5
+  --> $DIR/bad-lit-suffixes.rs:20:5
    |
 LL |     1234i1024;
    |     ^^^^^^^^^
@@ -63,7 +89,7 @@ LL |     1234i1024;
    = help: valid widths are 8, 16, 32, 64 and 128
 
 error: invalid width `1024` for float literal
-  --> $DIR/bad-lit-suffixes.rs:19:5
+  --> $DIR/bad-lit-suffixes.rs:21:5
    |
 LL |     1234f1024;
    |     ^^^^^^^^^
@@ -71,7 +97,7 @@ LL |     1234f1024;
    = help: valid widths are 32 and 64
 
 error: invalid width `1024` for float literal
-  --> $DIR/bad-lit-suffixes.rs:20:5
+  --> $DIR/bad-lit-suffixes.rs:22:5
    |
 LL |     1234.5f1024;
    |     ^^^^^^^^^^^
@@ -79,7 +105,7 @@ LL |     1234.5f1024;
    = help: valid widths are 32 and 64
 
 error: invalid suffix `suffix` for number literal
-  --> $DIR/bad-lit-suffixes.rs:22:5
+  --> $DIR/bad-lit-suffixes.rs:24:5
    |
 LL |     1234suffix;
    |     ^^^^^^^^^^ invalid suffix `suffix`
@@ -87,7 +113,7 @@ LL |     1234suffix;
    = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
 
 error: invalid suffix `suffix` for number literal
-  --> $DIR/bad-lit-suffixes.rs:23:5
+  --> $DIR/bad-lit-suffixes.rs:25:5
    |
 LL |     0b101suffix;
    |     ^^^^^^^^^^^ invalid suffix `suffix`
@@ -95,7 +121,7 @@ LL |     0b101suffix;
    = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
 
 error: invalid suffix `suffix` for float literal
-  --> $DIR/bad-lit-suffixes.rs:24:5
+  --> $DIR/bad-lit-suffixes.rs:26:5
    |
 LL |     1.0suffix;
    |     ^^^^^^^^^ invalid suffix `suffix`
@@ -103,12 +129,12 @@ LL |     1.0suffix;
    = help: valid suffixes are `f32` and `f64`
 
 error: invalid suffix `suffix` for float literal
-  --> $DIR/bad-lit-suffixes.rs:25:5
+  --> $DIR/bad-lit-suffixes.rs:27:5
    |
 LL |     1.0e10suffix;
    |     ^^^^^^^^^^^^ invalid suffix `suffix`
    |
    = help: valid suffixes are `f32` and `f64`
 
-error: aborting due to 16 previous errors
+error: aborting due to 20 previous errors
 
diff --git a/src/test/ui/parser/issues/issue-104088.rs b/src/test/ui/parser/issues/issue-104088.rs
new file mode 100644
index 00000000000..5f794fe2dc9
--- /dev/null
+++ b/src/test/ui/parser/issues/issue-104088.rs
@@ -0,0 +1,26 @@
+fn test() {
+    if let 123 = 123 { println!("yes"); }
+}
+
+fn test_2() {
+    let 1x = 123;
+    //~^ ERROR expected identifier, found number literal
+}
+
+fn test_3() {
+    let 2x: i32 = 123;
+    //~^ ERROR expected identifier, found number literal
+}
+
+fn test_4() {
+    if let 2e1 = 123 {
+        //~^ ERROR mismatched types
+    }
+}
+
+fn test_5() {
+    let 23name = 123;
+    //~^ ERROR expected identifier, found number literal
+}
+
+fn main() {}
diff --git a/src/test/ui/parser/issues/issue-104088.stderr b/src/test/ui/parser/issues/issue-104088.stderr
new file mode 100644
index 00000000000..ff4b4bdb695
--- /dev/null
+++ b/src/test/ui/parser/issues/issue-104088.stderr
@@ -0,0 +1,29 @@
+error: expected identifier, found number literal
+  --> $DIR/issue-104088.rs:6:9
+   |
+LL |     let 1x = 123;
+   |         ^^ identifiers cannot start with a number
+
+error: expected identifier, found number literal
+  --> $DIR/issue-104088.rs:11:9
+   |
+LL |     let 2x: i32 = 123;
+   |         ^^ identifiers cannot start with a number
+
+error: expected identifier, found number literal
+  --> $DIR/issue-104088.rs:22:9
+   |
+LL |     let 23name = 123;
+   |         ^^^^^^ identifiers cannot start with a number
+
+error[E0308]: mismatched types
+  --> $DIR/issue-104088.rs:16:12
+   |
+LL |     if let 2e1 = 123 {
+   |            ^^^   --- this expression has type `{integer}`
+   |            |
+   |            expected integer, found floating-point number
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/parser/macro/macro-doc-comments-1.stderr b/src/test/ui/parser/macro/macro-doc-comments-1.stderr
index 0ebf3d52b63..eaeb62d2cfd 100644
--- a/src/test/ui/parser/macro/macro-doc-comments-1.stderr
+++ b/src/test/ui/parser/macro/macro-doc-comments-1.stderr
@@ -9,6 +9,12 @@ LL |     //! Inner
    |     |
    |     no rules expected this token in macro call
    |     inner doc comments expand to `#![doc = "..."]`, which is what this macro attempted to match
+   |
+note: while trying to match `[`
+  --> $DIR/macro-doc-comments-1.rs:2:7
+   |
+LL |     (#[$outer:meta]) => ()
+   |       ^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/macro/macro-doc-comments-2.stderr b/src/test/ui/parser/macro/macro-doc-comments-2.stderr
index 346d865868d..1dcd95f6fad 100644
--- a/src/test/ui/parser/macro/macro-doc-comments-2.stderr
+++ b/src/test/ui/parser/macro/macro-doc-comments-2.stderr
@@ -9,6 +9,12 @@ LL |     /// Outer
    |     |
    |     no rules expected this token in macro call
    |     outer doc comments expand to `#[doc = "..."]`, which is what this macro attempted to match
+   |
+note: while trying to match `!`
+  --> $DIR/macro-doc-comments-2.rs:2:7
+   |
+LL |     (#![$inner:meta]) => ()
+   |       ^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/recover-fn-ptr-with-generics.rs b/src/test/ui/parser/recover-fn-ptr-with-generics.rs
new file mode 100644
index 00000000000..31de418be5f
--- /dev/null
+++ b/src/test/ui/parser/recover-fn-ptr-with-generics.rs
@@ -0,0 +1,31 @@
+fn main() {
+    type Predicate = fn<'a>(&'a str) -> bool;
+    //~^ ERROR function pointer types may not have generic parameters
+
+    type Identity = fn<T>(T) -> T;
+    //~^ ERROR function pointer types may not have generic parameters
+    //~| ERROR cannot find type `T` in this scope
+    //~| ERROR cannot find type `T` in this scope
+
+    let _: fn<const N: usize, 'e, Q, 'f>();
+    //~^ ERROR function pointer types may not have generic parameters
+
+    let _: for<'outer> fn<'inner>();
+    //~^ ERROR function pointer types may not have generic parameters
+
+    let _: for<> fn<'r>();
+    //~^ ERROR function pointer types may not have generic parameters
+
+    type Hmm = fn<>();
+    //~^ ERROR function pointer types may not have generic parameters
+
+    let _: extern fn<'a: 'static>();
+    //~^ ERROR function pointer types may not have generic parameters
+    //~| ERROR lifetime bounds cannot be used in this context
+
+    let _: for<'any> extern "C" fn<'u>();
+    //~^ ERROR function pointer types may not have generic parameters
+
+    type QuiteBroken = fn<const>();
+    //~^ ERROR expected identifier, found `>`
+}
diff --git a/src/test/ui/parser/recover-fn-ptr-with-generics.stderr b/src/test/ui/parser/recover-fn-ptr-with-generics.stderr
new file mode 100644
index 00000000000..1da9c18571b
--- /dev/null
+++ b/src/test/ui/parser/recover-fn-ptr-with-generics.stderr
@@ -0,0 +1,111 @@
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:2:24
+   |
+LL |     type Predicate = fn<'a>(&'a str) -> bool;
+   |                        ^^^^
+   |
+help: consider moving the lifetime parameter to a `for` parameter list
+   |
+LL -     type Predicate = fn<'a>(&'a str) -> bool;
+LL +     type Predicate = for<'a> fn(&'a str) -> bool;
+   |
+
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:5:23
+   |
+LL |     type Identity = fn<T>(T) -> T;
+   |                       ^^^
+
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:10:14
+   |
+LL |     let _: fn<const N: usize, 'e, Q, 'f>();
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider moving the lifetime parameters to a `for` parameter list
+   |
+LL -     let _: fn<const N: usize, 'e, Q, 'f>();
+LL +     let _: for<'e, 'f> fn();
+   |
+
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:13:26
+   |
+LL |     let _: for<'outer> fn<'inner>();
+   |                          ^^^^^^^^
+   |
+help: consider moving the lifetime parameter to the `for` parameter list
+   |
+LL -     let _: for<'outer> fn<'inner>();
+LL +     let _: for<'outer, 'inner> fn();
+   |
+
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:16:20
+   |
+LL |     let _: for<> fn<'r>();
+   |                    ^^^^
+   |
+help: consider moving the lifetime parameter to the `for` parameter list
+   |
+LL -     let _: for<> fn<'r>();
+LL +     let _: for<'r> fn();
+   |
+
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:19:18
+   |
+LL |     type Hmm = fn<>();
+   |                  ^^
+
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:22:21
+   |
+LL |     let _: extern fn<'a: 'static>();
+   |                     ^^^^^^^^^^^^^
+   |
+help: consider moving the lifetime parameter to a `for` parameter list
+   |
+LL -     let _: extern fn<'a: 'static>();
+LL +     let _: for<'a> extern fn();
+   |
+
+error: function pointer types may not have generic parameters
+  --> $DIR/recover-fn-ptr-with-generics.rs:26:35
+   |
+LL |     let _: for<'any> extern "C" fn<'u>();
+   |                                   ^^^^
+   |
+help: consider moving the lifetime parameter to the `for` parameter list
+   |
+LL -     let _: for<'any> extern "C" fn<'u>();
+LL +     let _: for<'any, 'u> extern "C" fn();
+   |
+
+error: expected identifier, found `>`
+  --> $DIR/recover-fn-ptr-with-generics.rs:29:32
+   |
+LL |     type QuiteBroken = fn<const>();
+   |                                ^ expected identifier
+
+error: lifetime bounds cannot be used in this context
+  --> $DIR/recover-fn-ptr-with-generics.rs:22:26
+   |
+LL |     let _: extern fn<'a: 'static>();
+   |                          ^^^^^^^
+
+error[E0412]: cannot find type `T` in this scope
+  --> $DIR/recover-fn-ptr-with-generics.rs:5:27
+   |
+LL |     type Identity = fn<T>(T) -> T;
+   |                           ^ not found in this scope
+
+error[E0412]: cannot find type `T` in this scope
+  --> $DIR/recover-fn-ptr-with-generics.rs:5:33
+   |
+LL |     type Identity = fn<T>(T) -> T;
+   |                                 ^ not found in this scope
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/src/test/ui/slowparse-bstring.rs b/src/test/ui/parser/slowparse-bstring.rs
index f3a6a668372..f3a6a668372 100644
--- a/src/test/ui/slowparse-bstring.rs
+++ b/src/test/ui/parser/slowparse-bstring.rs
diff --git a/src/test/ui/slowparse-string.rs b/src/test/ui/parser/slowparse-string.rs
index 6ebc61dae78..6ebc61dae78 100644
--- a/src/test/ui/slowparse-string.rs
+++ b/src/test/ui/parser/slowparse-string.rs
diff --git a/src/test/ui/range/issue-54505-no-std.rs b/src/test/ui/range/issue-54505-no-std.rs
index ab1a025b521..9f378b4836e 100644
--- a/src/test/ui/range/issue-54505-no-std.rs
+++ b/src/test/ui/range/issue-54505-no-std.rs
@@ -1,5 +1,3 @@
-// error-pattern: `#[panic_handler]` function required, but not found
-
 // Regression test for #54505 - range borrowing suggestion had
 // incorrect syntax (missing parentheses).
 
@@ -18,6 +16,10 @@ extern "C" fn eh_personality() {}
 #[lang = "eh_catch_typeinfo"]
 static EH_CATCH_TYPEINFO: u8 = 0;
 
+#[panic_handler]
+fn panic_handler() {}
+//~^ ERROR return type should be `!`
+//~| ERROR function should have one argument
 
 // take a reference to any built-in range
 fn take_range(_r: &impl RangeBounds<i8>) {}
diff --git a/src/test/ui/range/issue-54505-no-std.stderr b/src/test/ui/range/issue-54505-no-std.stderr
index c4e36b0b159..9fb0e54a8a9 100644
--- a/src/test/ui/range/issue-54505-no-std.stderr
+++ b/src/test/ui/range/issue-54505-no-std.stderr
@@ -1,7 +1,17 @@
-error: `#[panic_handler]` function required, but not found
+error: return type should be `!`
+  --> $DIR/issue-54505-no-std.rs:20:20
+   |
+LL | fn panic_handler() {}
+   |                    ^
+
+error: function should have one argument
+  --> $DIR/issue-54505-no-std.rs:20:1
+   |
+LL | fn panic_handler() {}
+   | ^^^^^^^^^^^^^^^^^^
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:27:16
+  --> $DIR/issue-54505-no-std.rs:29:16
    |
 LL |     take_range(0..1);
    |     ---------- ^^^^
@@ -13,13 +23,13 @@ LL |     take_range(0..1);
    = note: expected reference `&_`
                  found struct `Range<{integer}>`
 note: function defined here
-  --> $DIR/issue-54505-no-std.rs:23:4
+  --> $DIR/issue-54505-no-std.rs:25:4
    |
 LL | fn take_range(_r: &impl RangeBounds<i8>) {}
    |    ^^^^^^^^^^ -------------------------
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:32:16
+  --> $DIR/issue-54505-no-std.rs:34:16
    |
 LL |     take_range(1..);
    |     ---------- ^^^
@@ -31,13 +41,13 @@ LL |     take_range(1..);
    = note: expected reference `&_`
                  found struct `RangeFrom<{integer}>`
 note: function defined here
-  --> $DIR/issue-54505-no-std.rs:23:4
+  --> $DIR/issue-54505-no-std.rs:25:4
    |
 LL | fn take_range(_r: &impl RangeBounds<i8>) {}
    |    ^^^^^^^^^^ -------------------------
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:37:16
+  --> $DIR/issue-54505-no-std.rs:39:16
    |
 LL |     take_range(..);
    |     ---------- ^^
@@ -49,13 +59,13 @@ LL |     take_range(..);
    = note: expected reference `&_`
                  found struct `RangeFull`
 note: function defined here
-  --> $DIR/issue-54505-no-std.rs:23:4
+  --> $DIR/issue-54505-no-std.rs:25:4
    |
 LL | fn take_range(_r: &impl RangeBounds<i8>) {}
    |    ^^^^^^^^^^ -------------------------
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:42:16
+  --> $DIR/issue-54505-no-std.rs:44:16
    |
 LL |     take_range(0..=1);
    |     ---------- ^^^^^
@@ -67,13 +77,13 @@ LL |     take_range(0..=1);
    = note: expected reference `&_`
                  found struct `RangeInclusive<{integer}>`
 note: function defined here
-  --> $DIR/issue-54505-no-std.rs:23:4
+  --> $DIR/issue-54505-no-std.rs:25:4
    |
 LL | fn take_range(_r: &impl RangeBounds<i8>) {}
    |    ^^^^^^^^^^ -------------------------
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:47:16
+  --> $DIR/issue-54505-no-std.rs:49:16
    |
 LL |     take_range(..5);
    |     ---------- ^^^
@@ -85,13 +95,13 @@ LL |     take_range(..5);
    = note: expected reference `&_`
                  found struct `RangeTo<{integer}>`
 note: function defined here
-  --> $DIR/issue-54505-no-std.rs:23:4
+  --> $DIR/issue-54505-no-std.rs:25:4
    |
 LL | fn take_range(_r: &impl RangeBounds<i8>) {}
    |    ^^^^^^^^^^ -------------------------
 
 error[E0308]: mismatched types
-  --> $DIR/issue-54505-no-std.rs:52:16
+  --> $DIR/issue-54505-no-std.rs:54:16
    |
 LL |     take_range(..=42);
    |     ---------- ^^^^^
@@ -103,11 +113,11 @@ LL |     take_range(..=42);
    = note: expected reference `&_`
                  found struct `RangeToInclusive<{integer}>`
 note: function defined here
-  --> $DIR/issue-54505-no-std.rs:23:4
+  --> $DIR/issue-54505-no-std.rs:25:4
    |
 LL | fn take_range(_r: &impl RangeBounds<i8>) {}
    |    ^^^^^^^^^^ -------------------------
 
-error: aborting due to 7 previous errors
+error: aborting due to 8 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/resolve/issue-50599.rs b/src/test/ui/resolve/issue-50599.rs
index 78a20cf8ebb..72238a59198 100644
--- a/src/test/ui/resolve/issue-50599.rs
+++ b/src/test/ui/resolve/issue-50599.rs
@@ -2,5 +2,5 @@ fn main() {
     const N: u32 = 1_000;
     const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize; //~ ERROR cannot find value
     let mut digits = [0u32; M];
-    //~^ ERROR evaluation of constant value failed
+    //~^ constant
 }
diff --git a/src/test/ui/resolve/issue-50599.stderr b/src/test/ui/resolve/issue-50599.stderr
index 910deddd8bc..b07482c83cc 100644
--- a/src/test/ui/resolve/issue-50599.stderr
+++ b/src/test/ui/resolve/issue-50599.stderr
@@ -16,13 +16,12 @@ LL -     const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize;
 LL +     const M: usize = (f64::from(N) * LOG10_2) as usize;
    |
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/issue-50599.rs:4:29
    |
 LL |     let mut digits = [0u32; M];
-   |                             ^ referenced constant has errors
+   |                             ^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0080, E0425.
-For more information about an error, try `rustc --explain E0080`.
+For more information about this error, try `rustc --explain E0425`.
diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
index e017d04a5c9..96fe11911b7 100644
--- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
+++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
@@ -72,6 +72,12 @@ LL |     macro_rules! use_expr {
 ...
 LL |     use_expr!(let 0 = 1);
    |               ^^^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$e:expr`
+  --> $DIR/feature-gate.rs:61:10
+   |
+LL |         ($e:expr) => {
+   |          ^^^^^^^
 
 error[E0658]: `if let` guards are experimental
   --> $DIR/feature-gate.rs:7:12
diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
index feea1c254d8..7a43b71fc8b 100644
--- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
@@ -18,6 +18,12 @@ LL |     macro_rules! use_expr {
 ...
 LL |     use_expr!(let 0 = 1);
    |               ^^^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$e:expr`
+  --> $DIR/feature-gate.rs:50:10
+   |
+LL |         ($e:expr) => {
+   |          ^^^^^^^
 
 error[E0658]: `let` expressions in this position are unstable
   --> $DIR/feature-gate.rs:14:16
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
index f515ec198ad..36a09add4d3 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr
@@ -10,7 +10,7 @@ LL | impl const std::ops::Add for i32 {
    |
    = note: define and implement a trait or new type instead
 
-error[E0119]: conflicting implementations of trait `std::ops::Add` for type `Int`
+error[E0119]: conflicting implementations of trait `Add` for type `Int`
   --> $DIR/const-and-non-const-impl.rs:22:1
    |
 LL | impl std::ops::Add for Int {
diff --git a/src/test/ui/specialization/specialization-overlap-negative.stderr b/src/test/ui/specialization/specialization-overlap-negative.stderr
index fb3d9723aff..1fe4869ff54 100644
--- a/src/test/ui/specialization/specialization-overlap-negative.stderr
+++ b/src/test/ui/specialization/specialization-overlap-negative.stderr
@@ -8,7 +8,7 @@ LL | #![feature(specialization)]
    = help: consider using `min_specialization` instead, which is more stable and complete
    = note: `#[warn(incomplete_features)]` on by default
 
-error[E0751]: found both positive and negative implementation of trait `std::marker::Send` for type `TestType<_>`:
+error[E0751]: found both positive and negative implementation of trait `Send` for type `TestType<_>`:
   --> $DIR/specialization-overlap-negative.rs:9:1
    |
 LL | unsafe impl<T: Clone> Send for TestType<T> {}
diff --git a/src/test/ui/specialization/specialization-overlap.stderr b/src/test/ui/specialization/specialization-overlap.stderr
index 98926446765..098bf4a70ab 100644
--- a/src/test/ui/specialization/specialization-overlap.stderr
+++ b/src/test/ui/specialization/specialization-overlap.stderr
@@ -8,13 +8,13 @@ LL | #![feature(specialization)]
    = help: consider using `min_specialization` instead, which is more stable and complete
    = note: `#[warn(incomplete_features)]` on by default
 
-error[E0119]: conflicting implementations of trait `Foo` for type `std::vec::Vec<_>`
+error[E0119]: conflicting implementations of trait `Foo` for type `Vec<_>`
   --> $DIR/specialization-overlap.rs:5:1
    |
 LL | impl<T: Clone> Foo for T {}
    | ------------------------ first implementation here
 LL | impl<T> Foo for Vec<T> {}
-   | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::vec::Vec<_>`
+   | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Vec<_>`
 
 error[E0119]: conflicting implementations of trait `Bar` for type `(u8, u8)`
   --> $DIR/specialization-overlap.rs:9:1
diff --git a/src/test/ui/stats/hir-stats.stderr b/src/test/ui/stats/hir-stats.stderr
index 1521b692a75..9cadd59c3dc 100644
--- a/src/test/ui/stats/hir-stats.stderr
+++ b/src/test/ui/stats/hir-stats.stderr
@@ -119,40 +119,40 @@ hir-stats HIR STATS
 hir-stats Name                Accumulated Size         Count     Item Size
 hir-stats ----------------------------------------------------------------
 hir-stats ForeignItemRef            24 ( 0.3%)             1            24
-hir-stats Lifetime                  32 ( 0.4%)             1            32
-hir-stats Mod                       32 ( 0.4%)             1            32
+hir-stats Lifetime                  32 ( 0.3%)             1            32
+hir-stats Mod                       32 ( 0.3%)             1            32
 hir-stats ExprField                 40 ( 0.4%)             1            40
 hir-stats TraitItemRef              56 ( 0.6%)             2            28
 hir-stats Local                     64 ( 0.7%)             1            64
 hir-stats Param                     64 ( 0.7%)             2            32
 hir-stats InlineAsm                 72 ( 0.8%)             1            72
 hir-stats ImplItemRef               72 ( 0.8%)             2            36
-hir-stats Body                      96 ( 1.1%)             3            32
-hir-stats GenericArg                96 ( 1.1%)             4            24
-hir-stats - Type                      24 ( 0.3%)             1
-hir-stats - Lifetime                  72 ( 0.8%)             3
-hir-stats FieldDef                  96 ( 1.1%)             2            48
-hir-stats Arm                       96 ( 1.1%)             2            48
-hir-stats Stmt                      96 ( 1.1%)             3            32
-hir-stats - Local                     32 ( 0.4%)             1
-hir-stats - Semi                      32 ( 0.4%)             1
-hir-stats - Expr                      32 ( 0.4%)             1
+hir-stats Body                      96 ( 1.0%)             3            32
+hir-stats FieldDef                  96 ( 1.0%)             2            48
+hir-stats Arm                       96 ( 1.0%)             2            48
+hir-stats Stmt                      96 ( 1.0%)             3            32
+hir-stats - Local                     32 ( 0.3%)             1
+hir-stats - Semi                      32 ( 0.3%)             1
+hir-stats - Expr                      32 ( 0.3%)             1
 hir-stats FnDecl                   120 ( 1.3%)             3            40
 hir-stats Attribute                128 ( 1.4%)             4            32
+hir-stats GenericArg               128 ( 1.4%)             4            32
+hir-stats - Type                      32 ( 0.3%)             1
+hir-stats - Lifetime                  96 ( 1.0%)             3
 hir-stats GenericArgs              144 ( 1.6%)             3            48
-hir-stats Variant                  160 ( 1.8%)             2            80
+hir-stats Variant                  176 ( 1.9%)             2            88
 hir-stats GenericBound             192 ( 2.1%)             4            48
 hir-stats - Trait                    192 ( 2.1%)             4
 hir-stats WherePredicate           192 ( 2.1%)             3            64
 hir-stats - BoundPredicate           192 ( 2.1%)             3
-hir-stats Block                    288 ( 3.2%)             6            48
+hir-stats Block                    288 ( 3.1%)             6            48
 hir-stats Pat                      360 ( 3.9%)             5            72
 hir-stats - Wild                      72 ( 0.8%)             1
 hir-stats - Struct                    72 ( 0.8%)             1
 hir-stats - Binding                  216 ( 2.4%)             3
 hir-stats GenericParam             400 ( 4.4%)             5            80
 hir-stats Generics                 560 ( 6.1%)            10            56
-hir-stats Ty                       720 ( 7.9%)            15            48
+hir-stats Ty                       720 ( 7.8%)            15            48
 hir-stats - Ptr                       48 ( 0.5%)             1
 hir-stats - Rptr                      48 ( 0.5%)             1
 hir-stats - Path                     624 ( 6.8%)            13
@@ -169,10 +169,10 @@ hir-stats - Enum                      80 ( 0.9%)             1
 hir-stats - ExternCrate               80 ( 0.9%)             1
 hir-stats - ForeignMod                80 ( 0.9%)             1
 hir-stats - Impl                      80 ( 0.9%)             1
-hir-stats - Fn                       160 ( 1.8%)             2
+hir-stats - Fn                       160 ( 1.7%)             2
 hir-stats - Use                      400 ( 4.4%)             5
-hir-stats Path                   1_280 (14.0%)            32            40
-hir-stats PathSegment            1_920 (21.0%)            40            48
+hir-stats Path                   1_280 (13.9%)            32            40
+hir-stats PathSegment            1_920 (20.9%)            40            48
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  9_128
+hir-stats Total                  9_176
 hir-stats
diff --git a/src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs
new file mode 100644
index 00000000000..06cf243f1b4
--- /dev/null
+++ b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.rs
@@ -0,0 +1,4 @@
+fn main() {
+    1_u32.MAX();
+    //~^ ERROR no method named `MAX` found for type `u32` in the current scope
+}
diff --git a/src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr
new file mode 100644
index 00000000000..04e0511d788
--- /dev/null
+++ b/src/test/ui/suggestions/dont-suggest-ufcs-for-const.stderr
@@ -0,0 +1,15 @@
+error[E0599]: no method named `MAX` found for type `u32` in the current scope
+  --> $DIR/dont-suggest-ufcs-for-const.rs:2:11
+   |
+LL |     1_u32.MAX();
+   |     ------^^^--
+   |     |     |
+   |     |     this is an associated function, not a method
+   |     help: use associated function syntax instead: `u32::MAX()`
+   |
+   = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
+   = note: the candidate is defined in an impl for the type `u32`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed
new file mode 100644
index 00000000000..8d96cf590c3
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.fixed
@@ -0,0 +1,15 @@
+// run-rustfix
+
+#![allow(unused)]
+
+struct Foo<T>(T);
+
+impl<T> Foo<T> {
+    fn test() -> i32 { 1 }
+}
+
+fn main() {
+    let x = Box::new(Foo(1i32));
+    Foo::<i32>::test();
+    //~^ ERROR no method named `test` found for struct `Box<Foo<i32>>` in the current scope
+}
diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs
new file mode 100644
index 00000000000..186901f75a8
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.rs
@@ -0,0 +1,15 @@
+// run-rustfix
+
+#![allow(unused)]
+
+struct Foo<T>(T);
+
+impl<T> Foo<T> {
+    fn test() -> i32 { 1 }
+}
+
+fn main() {
+    let x = Box::new(Foo(1i32));
+    x.test();
+    //~^ ERROR no method named `test` found for struct `Box<Foo<i32>>` in the current scope
+}
diff --git a/src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr
new file mode 100644
index 00000000000..00fb96f0326
--- /dev/null
+++ b/src/test/ui/suggestions/suggest-assoc-fn-call-deref.stderr
@@ -0,0 +1,19 @@
+error[E0599]: no method named `test` found for struct `Box<Foo<i32>>` in the current scope
+  --> $DIR/suggest-assoc-fn-call-deref.rs:13:7
+   |
+LL |     x.test();
+   |     --^^^^--
+   |     | |
+   |     | this is an associated function, not a method
+   |     help: use associated function syntax instead: `Foo::<i32>::test()`
+   |
+   = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
+note: the candidate is defined in an impl for the type `Foo<T>`
+  --> $DIR/suggest-assoc-fn-call-deref.rs:8:5
+   |
+LL |     fn test() -> i32 { 1 }
+   |     ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/target-feature/invalid-attribute.stderr b/src/test/ui/target-feature/invalid-attribute.stderr
index 889ced9752b..a2adfc67f08 100644
--- a/src/test/ui/target-feature/invalid-attribute.stderr
+++ b/src/test/ui/target-feature/invalid-attribute.stderr
@@ -4,36 +4,6 @@ error: malformed `target_feature` attribute input
 LL | #[target_feature = "+sse2"]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[target_feature(enable = "name")]`
 
-error: the feature named `foo` is not valid for this target
-  --> $DIR/invalid-attribute.rs:19:18
-   |
-LL | #[target_feature(enable = "foo")]
-   |                  ^^^^^^^^^^^^^^ `foo` is not valid for this target
-
-error: malformed `target_feature` attribute input
-  --> $DIR/invalid-attribute.rs:22:18
-   |
-LL | #[target_feature(bar)]
-   |                  ^^^ help: must be of the form: `enable = ".."`
-
-error: malformed `target_feature` attribute input
-  --> $DIR/invalid-attribute.rs:24:18
-   |
-LL | #[target_feature(disable = "baz")]
-   |                  ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."`
-
-error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
-  --> $DIR/invalid-attribute.rs:28:1
-   |
-LL | #[target_feature(enable = "sse2")]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | fn bar() {}
-   | -------- not an `unsafe` function
-   |
-   = note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
-   = help: add `#![feature(target_feature_11)]` to the crate attributes to enable
-
 error: attribute should be applied to a function definition
   --> $DIR/invalid-attribute.rs:34:1
    |
@@ -92,12 +62,6 @@ LL |
 LL | trait Baz {}
    | ------------ not a function definition
 
-error: cannot use `#[inline(always)]` with `#[target_feature]`
-  --> $DIR/invalid-attribute.rs:67:1
-   |
-LL | #[inline(always)]
-   | ^^^^^^^^^^^^^^^^^
-
 error: attribute should be applied to a function definition
   --> $DIR/invalid-attribute.rs:85:5
    |
@@ -119,6 +83,42 @@ LL |
 LL |     || {};
    |     ----- not a function definition
 
+error: the feature named `foo` is not valid for this target
+  --> $DIR/invalid-attribute.rs:19:18
+   |
+LL | #[target_feature(enable = "foo")]
+   |                  ^^^^^^^^^^^^^^ `foo` is not valid for this target
+
+error: malformed `target_feature` attribute input
+  --> $DIR/invalid-attribute.rs:22:18
+   |
+LL | #[target_feature(bar)]
+   |                  ^^^ help: must be of the form: `enable = ".."`
+
+error: malformed `target_feature` attribute input
+  --> $DIR/invalid-attribute.rs:24:18
+   |
+LL | #[target_feature(disable = "baz")]
+   |                  ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."`
+
+error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
+  --> $DIR/invalid-attribute.rs:28:1
+   |
+LL | #[target_feature(enable = "sse2")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn bar() {}
+   | -------- not an `unsafe` function
+   |
+   = note: see issue #69098 <https://github.com/rust-lang/rust/issues/69098> for more information
+   = help: add `#![feature(target_feature_11)]` to the crate attributes to enable
+
+error: cannot use `#[inline(always)]` with `#[target_feature]`
+  --> $DIR/invalid-attribute.rs:67:1
+   |
+LL | #[inline(always)]
+   | ^^^^^^^^^^^^^^^^^
+
 error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions
   --> $DIR/invalid-attribute.rs:77:5
    |
diff --git a/src/test/ui/traits/issue-102989.rs b/src/test/ui/traits/issue-102989.rs
index 62f95272fbf..216cd78e56f 100644
--- a/src/test/ui/traits/issue-102989.rs
+++ b/src/test/ui/traits/issue-102989.rs
@@ -7,10 +7,8 @@ trait Sized { } //~ ERROR found duplicate lang item `sized`
 fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
     //~^ ERROR `self` parameter is only allowed in associated functions
     //~| ERROR cannot find type `Struct` in this scope
-    //~| ERROR mismatched types
     let x = x << 1;
-    //~^ ERROR the size for values of type `{integer}` cannot be known at compilation time
-    //~| ERROR cannot find value `x` in this scope
+    //~^ ERROR cannot find value `x` in this scope
 }
 
 fn main() {}
diff --git a/src/test/ui/traits/issue-102989.stderr b/src/test/ui/traits/issue-102989.stderr
index efe1a246774..7d0098fe885 100644
--- a/src/test/ui/traits/issue-102989.stderr
+++ b/src/test/ui/traits/issue-102989.stderr
@@ -13,7 +13,7 @@ LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
    |                      ^^^^^^ not found in this scope
 
 error[E0425]: cannot find value `x` in this scope
-  --> $DIR/issue-102989.rs:11:13
+  --> $DIR/issue-102989.rs:10:13
    |
 LL |     let x = x << 1;
    |             ^ help: a local variable with a similar name exists: `f`
@@ -28,32 +28,7 @@ LL | trait Sized { }
    = note: first definition in `core` loaded from SYSROOT/libcore-*.rlib
    = note: second definition in the local crate (`issue_102989`)
 
-error[E0277]: the size for values of type `{integer}` cannot be known at compilation time
-  --> $DIR/issue-102989.rs:11:15
-   |
-LL |     let x = x << 1;
-   |               ^^ doesn't have a size known at compile-time
-   |
-   = help: the trait `std::marker::Sized` is not implemented for `{integer}`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-102989.rs:7:42
-   |
-LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
-   |    ----------                            ^^^^ expected `&u32`, found `()`
-   |    |
-   |    implicitly returns `()` as its body has no tail or `return` expression
-   |
-note: consider returning one of these bindings
-  --> $DIR/issue-102989.rs:7:30
-   |
-LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 {
-   |                              ^
-...
-LL |     let x = x << 1;
-   |         ^
-
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0152, E0277, E0308, E0412, E0425.
+Some errors have detailed explanations: E0152, E0412, E0425.
 For more information about an error, try `rustc --explain E0152`.
diff --git a/src/test/ui/traits/issue-33140-hack-boundaries.stderr b/src/test/ui/traits/issue-33140-hack-boundaries.stderr
index 58286648d4f..80a502c6335 100644
--- a/src/test/ui/traits/issue-33140-hack-boundaries.stderr
+++ b/src/test/ui/traits/issue-33140-hack-boundaries.stderr
@@ -1,12 +1,12 @@
-error[E0119]: conflicting implementations of trait `Trait1` for type `(dyn std::marker::Send + 'static)`
+error[E0119]: conflicting implementations of trait `Trait1` for type `(dyn Send + 'static)`
   --> $DIR/issue-33140-hack-boundaries.rs:18:1
    |
 LL | impl Trait1 for dyn Send {}
    | ------------------------ first implementation here
 LL | impl Trait1 for dyn Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)`
 
-error[E0751]: found both positive and negative implementation of trait `Trait2` for type `(dyn std::marker::Send + 'static)`:
+error[E0751]: found both positive and negative implementation of trait `Trait2` for type `(dyn Send + 'static)`:
   --> $DIR/issue-33140-hack-boundaries.rs:25:1
    |
 LL | impl Trait2 for dyn Send {}
@@ -14,21 +14,21 @@ LL | impl Trait2 for dyn Send {}
 LL | impl !Trait2 for dyn Send {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here
 
-error[E0119]: conflicting implementations of trait `Trait3<(dyn std::marker::Sync + 'static)>` for type `(dyn std::marker::Send + 'static)`
+error[E0119]: conflicting implementations of trait `Trait3<(dyn Sync + 'static)>` for type `(dyn Send + 'static)`
   --> $DIR/issue-33140-hack-boundaries.rs:32:1
    |
 LL | impl Trait3<dyn Sync> for dyn Send {}
    | ---------------------------------- first implementation here
 LL | impl Trait3<dyn Sync> for dyn Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)`
 
-error[E0119]: conflicting implementations of trait `Trait4a` for type `(dyn std::marker::Send + 'static)`
+error[E0119]: conflicting implementations of trait `Trait4a` for type `(dyn Send + 'static)`
   --> $DIR/issue-33140-hack-boundaries.rs:39:1
    |
 LL | impl<T: ?Sized> Trait4a for T {}
    | ----------------------------- first implementation here
 LL | impl Trait4a for dyn Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)`
 
 error[E0119]: conflicting implementations of trait `Trait4b` for type `()`
   --> $DIR/issue-33140-hack-boundaries.rs:46:1
@@ -38,42 +38,42 @@ LL | impl Trait4b for () {}
 LL | impl Trait4b for () {}
    | ^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()`
 
-error[E0119]: conflicting implementations of trait `Trait4c` for type `(dyn Trait1 + std::marker::Send + 'static)`
+error[E0119]: conflicting implementations of trait `Trait4c` for type `(dyn Trait1 + Send + 'static)`
   --> $DIR/issue-33140-hack-boundaries.rs:53:1
    |
 LL | impl Trait4c for dyn Trait1 + Send {}
    | ---------------------------------- first implementation here
 LL | impl Trait4c for dyn Trait1 + Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Trait1 + std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Trait1 + Send + 'static)`
 
-error[E0119]: conflicting implementations of trait `Trait4d` for type `dyn std::marker::Send`
+error[E0119]: conflicting implementations of trait `Trait4d` for type `dyn Send`
   --> $DIR/issue-33140-hack-boundaries.rs:60:1
    |
 LL | impl<'a> Trait4d for dyn Send + 'a {}
    | ---------------------------------- first implementation here
 LL | impl<'a> Trait4d for dyn Send + 'a {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `dyn std::marker::Send`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `dyn Send`
 
-error[E0119]: conflicting implementations of trait `Trait5` for type `(dyn std::marker::Send + 'static)`
+error[E0119]: conflicting implementations of trait `Trait5` for type `(dyn Send + 'static)`
   --> $DIR/issue-33140-hack-boundaries.rs:67:1
    |
 LL | impl Trait5 for dyn Send {}
    | ------------------------ first implementation here
 LL | impl Trait5 for dyn Send where u32: Copy {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)`
 
 error: aborting due to 8 previous errors
 
 Some errors have detailed explanations: E0119, E0751.
 For more information about an error, try `rustc --explain E0119`.
 Future incompatibility report: Future breakage diagnostic:
-warning: conflicting implementations of trait `Trait0` for type `(dyn std::marker::Send + 'static)`: (E0119)
+warning: conflicting implementations of trait `Trait0` for type `(dyn Send + 'static)`: (E0119)
   --> $DIR/issue-33140-hack-boundaries.rs:10:1
    |
 LL | impl Trait0 for dyn Send {}
    | ------------------------ first implementation here
 LL | impl Trait0 for dyn Send {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
diff --git a/src/test/ui/traits/issue-33140.stderr b/src/test/ui/traits/issue-33140.stderr
index 392c56a282d..d31281f7256 100644
--- a/src/test/ui/traits/issue-33140.stderr
+++ b/src/test/ui/traits/issue-33140.stderr
@@ -1,20 +1,20 @@
-error[E0119]: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`
+error[E0119]: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`
   --> $DIR/issue-33140.rs:9:1
    |
 LL | impl Trait for dyn Send + Sync {
    | ------------------------------ first implementation here
 ...
 LL | impl Trait for dyn Sync + Send {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
 
-error[E0119]: conflicting implementations of trait `Trait2` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`
+error[E0119]: conflicting implementations of trait `Trait2` for type `(dyn Send + Sync + 'static)`
   --> $DIR/issue-33140.rs:22:1
    |
 LL | impl Trait2 for dyn Send + Sync {
    | ------------------------------- first implementation here
 ...
 LL | impl Trait2 for dyn Sync + Send + Sync {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
 
 error[E0592]: duplicate definitions with name `abc`
   --> $DIR/issue-33140.rs:29:5
diff --git a/src/test/ui/traits/item-privacy.stderr b/src/test/ui/traits/item-privacy.stderr
index 7f78b37ba84..f137a298a7f 100644
--- a/src/test/ui/traits/item-privacy.stderr
+++ b/src/test/ui/traits/item-privacy.stderr
@@ -162,9 +162,12 @@ error[E0223]: ambiguous associated type
 LL |     let _: S::C;
    |            ^^^^ help: use fully-qualified syntax: `<S as Trait>::C`
 
-error: associated type `A` is private
+error[E0624]: associated type `A` is private
   --> $DIR/item-privacy.rs:119:12
    |
+LL |         type A = u8;
+   |         ------ associated type defined here
+...
 LL |     let _: T::A;
    |            ^^^^ private associated type
 
diff --git a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr
index d7039e3db6b..a87acb1fb09 100644
--- a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr
+++ b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-clone.stderr
@@ -1,4 +1,4 @@
-error[E0751]: found both positive and negative implementation of trait `std::clone::Clone` for type `&mut MyType<'_>`:
+error[E0751]: found both positive and negative implementation of trait `Clone` for type `&mut MyType<'_>`:
   --> $DIR/pin-unsound-issue-66544-clone.rs:7:1
    |
 LL | impl<'a> Clone for &'a mut MyType<'a> {
diff --git a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr
index a0b62a8bab6..9185e8f8430 100644
--- a/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr
+++ b/src/test/ui/traits/negative-impls/pin-unsound-issue-66544-derefmut.stderr
@@ -1,4 +1,4 @@
-error[E0751]: found both positive and negative implementation of trait `std::ops::DerefMut` for type `&MyType<'_>`:
+error[E0751]: found both positive and negative implementation of trait `DerefMut` for type `&MyType<'_>`:
   --> $DIR/pin-unsound-issue-66544-derefmut.rs:12:1
    |
 LL | impl<'a> DerefMut for &'a MyType<'a> {
diff --git a/src/test/ui/traits/object/issue-33140-traitobject-crate.stderr b/src/test/ui/traits/object/issue-33140-traitobject-crate.stderr
index 0af4df2aecb..525401f9d69 100644
--- a/src/test/ui/traits/object/issue-33140-traitobject-crate.stderr
+++ b/src/test/ui/traits/object/issue-33140-traitobject-crate.stderr
@@ -1,10 +1,10 @@
-warning: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/issue-33140-traitobject-crate.rs:86:1
    |
 LL | unsafe impl Trait for dyn (::std::marker::Send) + Sync { }
    | ------------------------------------------------------ first implementation here
 LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
@@ -14,26 +14,26 @@ note: the lint level is defined here
 LL | #![warn(order_dependent_trait_objects)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-warning: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/issue-33140-traitobject-crate.rs:89:1
    |
 LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { }
    | ------------------------------------------------------------- first implementation here
 ...
 LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
 
-warning: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/issue-33140-traitobject-crate.rs:93:1
    |
 LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { }
    | ------------------------------------------------------ first implementation here
 ...
 LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send + Sync { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
@@ -41,13 +41,13 @@ LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send + Sync { }
 warning: 3 warnings emitted
 
 Future incompatibility report: Future breakage diagnostic:
-warning: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/issue-33140-traitobject-crate.rs:86:1
    |
 LL | unsafe impl Trait for dyn (::std::marker::Send) + Sync { }
    | ------------------------------------------------------ first implementation here
 LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
@@ -58,14 +58,14 @@ LL | #![warn(order_dependent_trait_objects)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Future breakage diagnostic:
-warning: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/issue-33140-traitobject-crate.rs:89:1
    |
 LL | unsafe impl Trait for dyn (::std::marker::Send) + Send + Sync { }
    | ------------------------------------------------------------- first implementation here
 ...
 LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
@@ -76,14 +76,14 @@ LL | #![warn(order_dependent_trait_objects)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Future breakage diagnostic:
-warning: conflicting implementations of trait `Trait` for type `(dyn std::marker::Send + std::marker::Sync + 'static)`: (E0119)
+warning: conflicting implementations of trait `Trait` for type `(dyn Send + Sync + 'static)`: (E0119)
   --> $DIR/issue-33140-traitobject-crate.rs:93:1
    |
 LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send { }
    | ------------------------------------------------------ first implementation here
 ...
 LL | unsafe impl Trait for dyn (::std::marker::Sync) + Send + Sync { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn std::marker::Send + std::marker::Sync + 'static)`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Send + Sync + 'static)`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #56484 <https://github.com/rust-lang/rust/issues/56484>
diff --git a/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr b/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr
index 910c5e29dac..e24ed695dc5 100644
--- a/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr
+++ b/src/test/ui/traits/overlap-not-permitted-for-builtin-trait.stderr
@@ -1,4 +1,4 @@
-error[E0119]: conflicting implementations of trait `std::marker::Send` for type `MyStruct`
+error[E0119]: conflicting implementations of trait `Send` for type `MyStruct`
   --> $DIR/overlap-not-permitted-for-builtin-trait.rs:7:1
    |
 LL | impl !Send for MyStruct {}
diff --git a/src/test/ui/traits/suggest-fully-qualified-closure.rs b/src/test/ui/traits/suggest-fully-qualified-closure.rs
new file mode 100644
index 00000000000..c077921c0bd
--- /dev/null
+++ b/src/test/ui/traits/suggest-fully-qualified-closure.rs
@@ -0,0 +1,24 @@
+// check-fail
+// known-bug: #103705
+// normalize-stderr-test "\[closure@.*\]" -> "[closure@]"
+// normalize-stderr-test "\+* ~" -> "+++ ~"
+
+// The output of this currently suggests writing a closure in the qualified path.
+
+trait MyTrait<T> {
+   fn lol<F:FnOnce()>(&self, f:F) -> u16;
+}
+
+struct Qqq;
+
+impl MyTrait<u32> for Qqq{
+   fn lol<F:FnOnce()>(&self, _f:F) -> u16 { 5 }
+}
+impl MyTrait<u64> for Qqq{
+   fn lol<F:FnOnce()>(&self, _f:F) -> u16 { 6 }
+}
+
+fn main() {
+    let q = Qqq;
+    q.lol(||());
+}
diff --git a/src/test/ui/traits/suggest-fully-qualified-closure.stderr b/src/test/ui/traits/suggest-fully-qualified-closure.stderr
new file mode 100644
index 00000000000..3df623c14c3
--- /dev/null
+++ b/src/test/ui/traits/suggest-fully-qualified-closure.stderr
@@ -0,0 +1,34 @@
+error[E0282]: type annotations needed
+  --> $DIR/suggest-fully-qualified-closure.rs:23:7
+   |
+LL |     q.lol(||());
+   |       ^^^
+   |
+help: try using a fully qualified path to specify the expected types
+   |
+LL |     <Qqq as MyTrait<T>>::lol::<[closure@]>(&q, ||());
+   |     +++ ~
+
+error[E0283]: type annotations needed
+  --> $DIR/suggest-fully-qualified-closure.rs:23:7
+   |
+LL |     q.lol(||());
+   |       ^^^
+   |
+note: multiple `impl`s satisfying `Qqq: MyTrait<_>` found
+  --> $DIR/suggest-fully-qualified-closure.rs:14:1
+   |
+LL | impl MyTrait<u32> for Qqq{
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | impl MyTrait<u64> for Qqq{
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a fully qualified path to specify the expected types
+   |
+LL |     <Qqq as MyTrait<T>>::lol::<[closure@]>(&q, ||());
+   |     +++ ~
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0282, E0283.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/src/test/ui/type/type-dependent-def-issue-49241.rs b/src/test/ui/type/type-dependent-def-issue-49241.rs
index f37f093d9ed..caf5bade5c7 100644
--- a/src/test/ui/type/type-dependent-def-issue-49241.rs
+++ b/src/test/ui/type/type-dependent-def-issue-49241.rs
@@ -2,5 +2,5 @@ fn main() {
     let v = vec![0];
     const l: usize = v.count(); //~ ERROR attempt to use a non-constant value in a constant
     let s: [u32; l] = v.into_iter().collect();
-    //~^ERROR evaluation of constant value failed
+    //~^ constant
 }
diff --git a/src/test/ui/type/type-dependent-def-issue-49241.stderr b/src/test/ui/type/type-dependent-def-issue-49241.stderr
index 02f267c6c8d..af16a6e8f84 100644
--- a/src/test/ui/type/type-dependent-def-issue-49241.stderr
+++ b/src/test/ui/type/type-dependent-def-issue-49241.stderr
@@ -6,13 +6,12 @@ LL |     const l: usize = v.count();
    |     |
    |     help: consider using `let` instead of `const`: `let l`
 
-error[E0080]: evaluation of constant value failed
+note: erroneous constant used
   --> $DIR/type-dependent-def-issue-49241.rs:4:18
    |
 LL |     let s: [u32; l] = v.into_iter().collect();
-   |                  ^ referenced constant has errors
+   |                  ^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0080, E0435.
-For more information about an error, try `rustc --explain E0080`.
+For more information about this error, try `rustc --explain E0435`.
diff --git a/src/test/ui/typeck/issue-103899.rs b/src/test/ui/typeck/issue-103899.rs
new file mode 100644
index 00000000000..9d5341dab42
--- /dev/null
+++ b/src/test/ui/typeck/issue-103899.rs
@@ -0,0 +1,33 @@
+// check-fail
+// failure-status: 101
+// normalize-stderr-test "note: .*" -> ""
+// normalize-stderr-test "thread 'rustc' .*" -> ""
+// normalize-stderr-test " .*\n" -> ""
+// normalize-stderr-test "  .*\n" -> ""
+// known-bug: #103899
+
+trait BaseWithAssoc {
+    type Assoc;
+}
+
+trait WrapperWithAssoc {
+    type BaseAssoc: BaseWithAssoc;
+}
+
+struct Wrapper<B> {
+    inner: B,
+}
+
+struct ProjectToBase<T: BaseWithAssoc> {
+    data_type_h: T::Assoc,
+}
+
+struct DoubleProject<L: WrapperWithAssoc> {
+    buffer: Wrapper<ProjectToBase<L::BaseAssoc>>,
+}
+
+fn trigger<L: WrapperWithAssoc<BaseAssoc = ()>>() -> DoubleProject<L> {
+    loop {}
+}
+
+fn main() {}
diff --git a/src/test/ui/typeck/issue-103899.stderr b/src/test/ui/typeck/issue-103899.stderr
new file mode 100644
index 00000000000..836c6ee486f
--- /dev/null
+++ b/src/test/ui/typeck/issue-103899.stderr
@@ -0,0 +1,12 @@
+
+stack
+error:
+
+
+
+
+
+
+
+
+query#0#1end
\ No newline at end of file
diff --git a/src/test/ui/underscore-ident-matcher.stderr b/src/test/ui/underscore-ident-matcher.stderr
index 241c3d3d8ce..b0e4d88f671 100644
--- a/src/test/ui/underscore-ident-matcher.stderr
+++ b/src/test/ui/underscore-ident-matcher.stderr
@@ -6,6 +6,12 @@ LL | macro_rules! identity {
 ...
 LL |     let identity!(_) = 10;
    |                   ^ no rules expected this token in macro call
+   |
+note: while trying to match meta-variable `$i:ident`
+  --> $DIR/underscore-ident-matcher.rs:2:6
+   |
+LL |     ($i: ident) => (
+   |      ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/use/use-crate-self.rs b/src/test/ui/use/use-crate-self.rs
new file mode 100644
index 00000000000..65ab948147c
--- /dev/null
+++ b/src/test/ui/use/use-crate-self.rs
@@ -0,0 +1,4 @@
+use crate::{self};
+        //~^ ERROR crate root imports need to be explicitly named: `use crate as name;`
+
+fn main() {}
diff --git a/src/test/ui/use/use-crate-self.stderr b/src/test/ui/use/use-crate-self.stderr
new file mode 100644
index 00000000000..dd4036bfff4
--- /dev/null
+++ b/src/test/ui/use/use-crate-self.stderr
@@ -0,0 +1,8 @@
+error: crate root imports need to be explicitly named: `use crate as name;`
+  --> $DIR/use-crate-self.rs:1:13
+   |
+LL | use crate::{self};
+   |             ^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject a3dfea71ca0c888a88111086898aa833c291d49
+Subproject 16b097879b6f117c8ae698aab054c87f26ff325
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
index 073e4af1318..df92579a85d 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
@@ -73,12 +73,21 @@ impl EarlyLintPass for AlmostCompleteLetterRange {
 }
 
 fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
-    if let ExprKind::Lit(start_lit) = &start.peel_parens().kind
-        && let ExprKind::Lit(end_lit) = &end.peel_parens().kind
+    if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind
+        && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind
         && matches!(
-            (&start_lit.kind, &end_lit.kind),
-            (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z'))
-            | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z'))
+            (
+                LitKind::from_token_lit(start_token_lit),
+                LitKind::from_token_lit(end_token_lit),
+            ),
+            (
+                Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
+                Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
+            )
+            | (
+                Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
+                Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
+            )
         )
         && !in_external_macro(cx.sess(), span)
     {
diff --git a/src/tools/clippy/clippy_lints/src/int_plus_one.rs b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
index 33491da3fc5..f793abdfda3 100644
--- a/src/tools/clippy/clippy_lints/src/int_plus_one.rs
+++ b/src/tools/clippy/clippy_lints/src/int_plus_one.rs
@@ -2,7 +2,8 @@
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
-use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind};
+use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind};
+use rustc_ast::token;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -52,8 +53,8 @@ enum Side {
 
 impl IntPlusOne {
     #[expect(clippy::cast_sign_loss)]
-    fn check_lit(lit: &Lit, target_value: i128) -> bool {
-        if let LitKind::Int(value, ..) = lit.kind {
+    fn check_lit(token_lit: token::Lit, target_value: i128) -> bool {
+        if let Ok(LitKind::Int(value, ..)) = LitKind::from_token_lit(token_lit) {
             return value == (target_value as u128);
         }
         false
@@ -65,11 +66,11 @@ impl IntPlusOne {
             (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
                 match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
                     // `-1 + x`
-                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
+                    (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => {
                         Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
                     },
                     // `x - 1`
-                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+                    (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
                         Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
                     },
                     _ => None,
@@ -81,10 +82,10 @@ impl IntPlusOne {
             {
                 match (&rhslhs.kind, &rhsrhs.kind) {
                     // `y + 1` and `1 + y`
-                    (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
+                    (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => {
                         Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
                     },
-                    (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+                    (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
                         Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
                     },
                     _ => None,
@@ -96,10 +97,10 @@ impl IntPlusOne {
             {
                 match (&lhslhs.kind, &lhsrhs.kind) {
                     // `1 + x` and `x + 1`
-                    (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => {
+                    (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => {
                         Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
                     },
-                    (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+                    (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
                         Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
                     },
                     _ => None,
@@ -109,11 +110,11 @@ impl IntPlusOne {
             (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
                 match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
                     // `-1 + y`
-                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => {
+                    (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => {
                         Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
                     },
                     // `y - 1`
-                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => {
+                    (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
                         Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
                     },
                     _ => None,
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index 25f19b9c6e6..3a7b7835c99 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -5,11 +5,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::numeric_literal::{NumericLiteral, Radix};
 use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
-use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
+use rustc_ast::ast::{Expr, ExprKind, LitKind};
+use rustc_ast::token;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
 use std::iter;
 
 declare_clippy_lint! {
@@ -236,8 +238,8 @@ impl EarlyLintPass for LiteralDigitGrouping {
             return;
         }
 
-        if let ExprKind::Lit(ref lit) = expr.kind {
-            self.check_lit(cx, lit);
+        if let ExprKind::Lit(lit) = expr.kind {
+            self.check_lit(cx, lit, expr.span);
         }
     }
 }
@@ -252,12 +254,13 @@ impl LiteralDigitGrouping {
         }
     }
 
-    fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
+    fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
         if_chain! {
-            if let Some(src) = snippet_opt(cx, lit.span);
-            if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit);
+            if let Some(src) = snippet_opt(cx, span);
+            if let Ok(lit_kind) = LitKind::from_token_lit(lit);
+            if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind);
             then {
-                if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) {
+                if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) {
                     return;
                 }
 
@@ -293,14 +296,14 @@ impl LiteralDigitGrouping {
                         | WarningType::InconsistentDigitGrouping
                         | WarningType::UnusualByteGroupings
                         | WarningType::LargeDigitGroups => {
-                            !lit.span.from_expansion()
+                            !span.from_expansion()
                         }
                         WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
                             true
                         }
                     };
                     if should_warn {
-                        warning_type.display(num_lit.format(), cx, lit.span);
+                        warning_type.display(num_lit.format(), cx, span);
                     }
                 }
             }
@@ -458,8 +461,8 @@ impl EarlyLintPass for DecimalLiteralRepresentation {
             return;
         }
 
-        if let ExprKind::Lit(ref lit) = expr.kind {
-            self.check_lit(cx, lit);
+        if let ExprKind::Lit(lit) = expr.kind {
+            self.check_lit(cx, lit, expr.span);
         }
     }
 }
@@ -469,19 +472,20 @@ impl DecimalLiteralRepresentation {
     pub fn new(threshold: u64) -> Self {
         Self { threshold }
     }
-    fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
+    fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
         // Lint integral literals.
         if_chain! {
-            if let LitKind::Int(val, _) = lit.kind;
-            if let Some(src) = snippet_opt(cx, lit.span);
-            if let Some(num_lit) = NumericLiteral::from_lit(&src, lit);
+            if let Ok(lit_kind) = LitKind::from_token_lit(lit);
+            if let LitKind::Int(val, _) = lit_kind;
+            if let Some(src) = snippet_opt(cx, span);
+            if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind);
             if num_lit.radix == Radix::Decimal;
             if val >= u128::from(self.threshold);
             then {
                 let hex = format!("{val:#X}");
                 let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
                 let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
-                    warning_type.display(num_lit.format(), cx, lit.span);
+                    warning_type.display(num_lit.format(), cx, span);
                 });
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 6806c146696..4877cee0cc1 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -157,10 +157,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
             && def.variants.len() > 1
         {
             let mut iter = def.variants.iter().filter_map(|v| {
-                let id = cx.tcx.hir().local_def_id(v.id);
-                (matches!(v.data, hir::VariantData::Unit(_))
+                let id = cx.tcx.hir().local_def_id(v.hir_id);
+                (matches!(v.data, hir::VariantData::Unit(..))
                     && v.ident.as_str().starts_with('_')
-                    && is_doc_hidden(cx.tcx.hir().attrs(v.id)))
+                    && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)))
                 .then_some((id, v.span))
             });
             if let Some((id, span)) = iter.next()
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs
index 27e7f8505eb..eda4376f200 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/literal_suffix.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use rustc_ast::ast::Lit;
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
+use rustc_span::Span;
 
 use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX};
 
-pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) {
+pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffix: &str, sugg_type: &str) {
     let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else {
         return; // It's useless so shouldn't lint.
     };
@@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s
             span_lint_and_sugg(
                 cx,
                 SEPARATED_LITERAL_SUFFIX,
-                lit.span,
+                lit_span,
                 &format!("{sugg_type} type suffix should not be separated by an underscore"),
                 "remove the underscore",
                 format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]),
@@ -25,7 +25,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s
             span_lint_and_sugg(
                 cx,
                 UNSEPARATED_LITERAL_SUFFIX,
-                lit.span,
+                lit_span,
                 &format!("{sugg_type} type suffix should be separated by an underscore"),
                 "add an underscore",
                 format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]),
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
index 263ee1e945a..ddb8b9173a5 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mixed_case_hex_literals.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint;
-use rustc_ast::ast::Lit;
 use rustc_lint::EarlyContext;
+use rustc_span::Span;
 
 use super::MIXED_CASE_HEX_LITERALS;
 
-pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) {
+pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) {
     let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else {
         return; // It's useless so shouldn't lint.
     };
@@ -23,7 +23,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &s
             span_lint(
                 cx,
                 MIXED_CASE_HEX_LITERALS,
-                lit.span,
+                lit_span,
                 "inconsistent casing in hexadecimal literal",
             );
             break;
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
index c8227ca4450..78be6b9e23f 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -9,7 +9,8 @@ mod zero_prefixed_literal;
 
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::source::snippet_opt;
-use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
+use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
+use rustc_ast::token;
 use rustc_ast::visit::FnKind;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
@@ -374,42 +375,43 @@ impl EarlyLintPass for MiscEarlyLints {
             return;
         }
 
-        if let ExprKind::Lit(ref lit) = expr.kind {
-            MiscEarlyLints::check_lit(cx, lit);
+        if let ExprKind::Lit(lit) = expr.kind {
+            MiscEarlyLints::check_lit(cx, lit, expr.span);
         }
         double_neg::check(cx, expr);
     }
 }
 
 impl MiscEarlyLints {
-    fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
+    fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
         // We test if first character in snippet is a number, because the snippet could be an expansion
         // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
         // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
         // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
         // FIXME: Find a better way to detect those cases.
-        let lit_snip = match snippet_opt(cx, lit.span) {
+        let lit_snip = match snippet_opt(cx, span) {
             Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
             _ => return,
         };
 
-        if let LitKind::Int(value, lit_int_type) = lit.kind {
+        let lit_kind = LitKind::from_token_lit(lit);
+        if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind {
             let suffix = match lit_int_type {
                 LitIntType::Signed(ty) => ty.name_str(),
                 LitIntType::Unsigned(ty) => ty.name_str(),
                 LitIntType::Unsuffixed => "",
             };
-            literal_suffix::check(cx, lit, &lit_snip, suffix, "integer");
+            literal_suffix::check(cx, span, &lit_snip, suffix, "integer");
             if lit_snip.starts_with("0x") {
-                mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip);
+                mixed_case_hex_literals::check(cx, span, suffix, &lit_snip);
             } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
                 // nothing to do
             } else if value != 0 && lit_snip.starts_with('0') {
-                zero_prefixed_literal::check(cx, lit, &lit_snip);
+                zero_prefixed_literal::check(cx, span, &lit_snip);
             }
-        } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
+        } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind {
             let suffix = float_ty.name_str();
-            literal_suffix::check(cx, lit, &lit_snip, suffix, "float");
+            literal_suffix::check(cx, span, &lit_snip, suffix, "float");
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs
index 9ead43ea4a4..4f9578d1b25 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs
@@ -1,20 +1,20 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use rustc_ast::ast::Lit;
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
+use rustc_span::Span;
 
 use super::ZERO_PREFIXED_LITERAL;
 
-pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) {
+pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) {
     let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0');
     span_lint_and_then(
         cx,
         ZERO_PREFIXED_LITERAL,
-        lit.span,
+        lit_span,
         "this is a decimal constant",
         |diag| {
             diag.span_suggestion(
-                lit.span,
+                lit_span,
                 "if you mean to use a decimal constant, remove the `0` to avoid confusion",
                 trimmed_lit_snip.to_string(),
                 Applicability::MaybeIncorrect,
@@ -22,7 +22,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) {
             // do not advise to use octal form if the literal cannot be expressed in base 8.
             if !lit_snip.contains(|c| c == '8' || c == '9') {
                 diag.span_suggestion(
-                    lit.span,
+                    lit_span,
                     "if you mean to use an octal constant, use `0o`",
                     format!("0o{trimmed_lit_snip}"),
                     Applicability::MaybeIncorrect,
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 2a63681db60..6fd100762b4 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
     }
 
     fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
-        let attrs = cx.tcx.hir().attrs(v.id);
+        let attrs = cx.tcx.hir().attrs(v.hir_id);
         if !is_from_proc_macro(cx, v) {
             self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
         }
diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs
index f380a506582..2a7159764e4 100644
--- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs
+++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs
@@ -56,11 +56,11 @@ impl EarlyLintPass for OctalEscapes {
             return;
         }
 
-        if let ExprKind::Lit(lit) = &expr.kind {
-            if matches!(lit.token_lit.kind, LitKind::Str) {
-                check_lit(cx, &lit.token_lit, lit.span, true);
-            } else if matches!(lit.token_lit.kind, LitKind::ByteStr) {
-                check_lit(cx, &lit.token_lit, lit.span, false);
+        if let ExprKind::Lit(token_lit) = &expr.kind {
+            if matches!(token_lit.kind, LitKind::Str) {
+                check_lit(cx, &token_lit, expr.span, true);
+            } else if matches!(token_lit.kind, LitKind::ByteStr) {
+                check_lit(cx, &token_lit, expr.span, false);
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs
index e6e3ad05ad7..bee4a33fb4a 100644
--- a/src/tools/clippy/clippy_lints/src/precedence.rs
+++ b/src/tools/clippy/clippy_lints/src/precedence.rs
@@ -1,7 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
-use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
+use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_ast::token;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -120,7 +121,7 @@ impl EarlyLintPass for Precedence {
             if_chain! {
                 if !all_odd;
                 if let ExprKind::Lit(lit) = &arg.kind;
-                if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind;
+                if let token::LitKind::Integer | token::LitKind::Float = &lit.kind;
                 then {
                     let mut applicability = Applicability::MachineApplicable;
                     span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs
index 3164937293b..3c1998d0237 100644
--- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind};
+use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -33,14 +33,14 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
     if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind
         && let method_name = name_ident.ident.name.as_str()
         && (method_name == "ceil" || method_name == "round" || method_name == "floor")
-        && let ExprKind::Lit(spanned) = &receiver.kind
-        && let LitKind::Float(symbol, ty) = spanned.kind {
-            let f = symbol.as_str().parse::<f64>().unwrap();
-            let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty {
-                ty.name_str()
-            } else {
-                ""
-            };
+        && let ExprKind::Lit(token_lit) = &receiver.kind
+        && token_lit.is_semantic_float() {
+            let f = token_lit.symbol.as_str().parse::<f64>().unwrap();
+            let mut f_str = token_lit.symbol.to_string();
+            match token_lit.suffix {
+                Some(suffix) => f_str.push_str(suffix.as_str()),
+                None => {}
+            }
             if f.fract() == 0.0 {
                 Some((method_name, f_str))
             } else {
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 0133997560e..73d1ba727c8 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -152,7 +152,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         },
         (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
         (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
-        (Lit(l), Lit(r)) => l.kind == r.kind,
+        (Lit(l), Lit(r)) => l == r,
         (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
         (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
         (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 9a682fbe604..d13b34a66cc 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -199,12 +199,12 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<
 pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
     let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
     matches!(
-        name.as_str(),
-        "core_panic_macro"
-            | "std_panic_macro"
-            | "core_panic_2015_macro"
-            | "std_panic_2015_macro"
-            | "core_panic_2021_macro"
+        name,
+        sym::core_panic_macro
+            | sym::std_panic_macro
+            | sym::core_panic_2015_macro
+            | sym::std_panic_2015_macro
+            | sym::core_panic_2021_macro
     )
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
index c5dcd7b31f5..42bdfd4827f 100644
--- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs
+++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
@@ -1,4 +1,4 @@
-use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
+use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 use std::iter;
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -46,10 +46,6 @@ pub struct NumericLiteral<'a> {
 }
 
 impl<'a> NumericLiteral<'a> {
-    pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
-        NumericLiteral::from_lit_kind(src, &lit.kind)
-    }
-
     pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
         let unsigned_src = src.strip_prefix('-').map_or(src, |s| s);
         if lit_kind.is_numeric()
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
index da5bc38b3b6..d8b6e3f1262 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -4,11 +4,11 @@ error[E0080]: evaluation of `main::{constant#3}` failed
 LL |     const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
    |              ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
 
-error[E0080]: erroneous constant used
+note: erroneous constant used
   --> $DIR/indexing_slicing_index.rs:31:5
    |
 LL |     const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
-   |     ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+   |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: indexing may panic
   --> $DIR/indexing_slicing_index.rs:22:5
@@ -65,6 +65,6 @@ error[E0080]: evaluation of constant value failed
 LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
    |                        ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
 
-error: aborting due to 9 previous errors
+error: aborting due to 8 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index 857feb77325..3842a649c6f 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -37,10 +37,8 @@ impl Lint {
     }
 
     fn is_ignored(&self) -> bool {
-        self.doc
-            .iter()
-            .filter(|line| line.starts_with("```rust"))
-            .all(|line| line.contains(",ignore"))
+        let blocks: Vec<_> = self.doc.iter().filter(|line| line.starts_with("```rust")).collect();
+        !blocks.is_empty() && blocks.iter().all(|line| line.contains(",ignore"))
     }
 
     /// Checks the doc style of the lint.
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 607ffe0cc59..138a69974e1 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -67,10 +67,10 @@ jobs:
         shell: bash
         run: |
           if [[ ${{ github.event_name }} == 'schedule' ]]; then
-            ./miri toolchain HEAD --host ${{ matrix.host_target }}
-          else
-            ./miri toolchain "" --host ${{ matrix.host_target }}
+            echo "Building against latest rustc git version"
+            git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1 > rust-version
           fi
+          ./miri toolchain --host ${{ matrix.host_target }}
 
       - name: Show Rust version
         run: |
diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md
index 5c41547616e..5f46e2af0f9 100644
--- a/src/tools/miri/CONTRIBUTING.md
+++ b/src/tools/miri/CONTRIBUTING.md
@@ -150,7 +150,8 @@ is set the `MIRI_LIB_SRC` environment variable to the `library` folder of a
 `rust-lang/rust` repository checkout. Note that changing files in that directory
 does not automatically trigger a re-build of the standard library; you have to
 clear the Miri build cache manually (on Linux, `rm -rf ~/.cache/miri`;
-and on Windows, `rmdir /S "%LOCALAPPDATA%\rust-lang\miri\cache"`).
+on Windows, `rmdir /S "%LOCALAPPDATA%\rust-lang\miri\cache"`;
+and on macOS, `rm -rf ~/Library/Caches/org.rust-lang.miri`).
 
 ### Benchmarking
 
@@ -208,23 +209,6 @@ We described above the simplest way to get a working build environment for Miri,
 which is to use the version of rustc indicated by `rustc-version`. But
 sometimes, that is not enough.
 
-### Updating `rustc-version`
-
-The `rustc-version` file is regularly updated to keep Miri close to the latest
-version of rustc. Usually, new contributors do not have to worry about this. But
-sometimes a newer rustc is needed for a patch, and sometimes Miri needs fixing
-for changes in rustc. In both cases, `rustc-version` needs updating.
-
-To update the `rustc-version` file and install the latest rustc, you can run:
-```
-./miri toolchain HEAD
-```
-
-Now edit Miri until `./miri test` passes, and submit a PR. Generally, it is
-preferred to separate updating `rustc-version` and doing what it takes to get
-Miri working again, from implementing new features that rely on the updated
-rustc. This avoids blocking all Miri development on landing a big PR.
-
 ### Building Miri with a locally built rustc
 
 [building Miri with a locally built rustc]: #building-miri-with-a-locally-built-rustc
@@ -282,13 +266,13 @@ With this, you should now have a working development setup! See
 ## Advanced topic: Syncing with the rustc repo
 
 We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit
-changes between the rustc and Miri repositories. For now, a fork of josh needs to be built
-from source. This downloads and runs josh:
+changes between the rustc and Miri repositories. For now, the latest git version
+of josh needs to be built from source. This downloads and runs josh:
 
 ```sh
-git clone https://github.com/RalfJung/josh
+git clone https://github.com/josh-project/josh
 cd josh
-cargo run --release -p josh-proxy -- --local=$(pwd)/local --remote=https://github.com --no-background
+cargo run --release -p josh-proxy -- --local=local --remote=https://github.com --no-background
 ```
 
 ### Importing changes from the rustc repo
@@ -298,9 +282,10 @@ We assume we start on an up-to-date master branch in the Miri repo.
 
 ```sh
 # Fetch and merge rustc side of the history. Takes ca 5 min the first time.
+# This will also update the 'rustc-version' file.
 ./miri rustc-pull
-# Update toolchain reference and apply formatting.
-./miri toolchain HEAD && ./miri fmt
+# Update local toolchain and apply formatting.
+./miri toolchain && ./miri fmt
 git commit -am "rustup"
 ```
 
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index 343cf0eaba2..3c890df08cc 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -65,6 +65,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "bstr"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd"
+dependencies = [
+ "memchr",
+ "once_cell",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
 name = "camino"
 version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -146,20 +158,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "crossbeam"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
-dependencies = [
- "cfg-if",
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-epoch",
- "crossbeam-queue",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "crossbeam-channel"
 version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -170,41 +168,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "crossbeam-deque"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
-dependencies = [
- "cfg-if",
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
-dependencies = [
- "autocfg",
- "cfg-if",
- "crossbeam-utils",
- "memoffset",
- "once_cell",
- "scopeguard",
-]
-
-[[package]]
-name = "crossbeam-queue"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
 name = "crossbeam-utils"
 version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -244,6 +207,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
 name = "getrandom"
 version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -386,15 +358,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "memoffset"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
 name = "miniz_oxide"
 version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -561,12 +524,27 @@ dependencies = [
 ]
 
 [[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+
+[[package]]
 name = "regex-syntax"
 version = "0.6.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
 
 [[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
 name = "rustc-demangle"
 version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -678,6 +656,20 @@ dependencies = [
 ]
 
 [[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
 name = "termcolor"
 version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -739,20 +731,22 @@ dependencies = [
 
 [[package]]
 name = "ui_test"
-version = "0.3.1"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d1f546a5883ae78da735bba529ec1116661e2f73582f23920d994dc97da3a22"
+checksum = "bf4559da3fe6b481f8674a29379677cb9606cd6f75fc254a2c9834c55638503d"
 dependencies = [
+ "bstr",
  "cargo_metadata",
  "color-eyre",
  "colored",
- "crossbeam",
+ "crossbeam-channel",
  "diff",
  "lazy_static",
  "regex",
  "rustc_version",
  "serde",
  "serde_json",
+ "tempfile",
 ]
 
 [[package]]
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index 02485dab74c..65ef92e21d6 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -40,7 +40,7 @@ libloading = "0.7"
 
 [dev-dependencies]
 colored = "2"
-ui_test = "0.3.1"
+ui_test = "0.4"
 rustc_version = "0.4"
 # Features chosen to match those required by env_logger, to avoid rebuilds
 regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] }
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 1185525f686..e9738cbc515 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -432,7 +432,9 @@ Moreover, Miri recognizes some environment variables:
   must point to the `library` subdirectory of a `rust-lang/rust` repository
   checkout. Note that changing files in that directory does not automatically
   trigger a re-build of the standard library; you have to clear the Miri build
-  cache manually (on Linux, `rm -rf ~/.cache/miri`).
+  cache manually (on Linux, `rm -rf ~/.cache/miri`;
+  on Windows, `rmdir /S "%LOCALAPPDATA%\rust-lang\miri\cache"`;
+  and on macOS, `rm -rf ~/Library/Caches/org.rust-lang.miri`).
 * `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When
   using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the
   automatically created sysroot. For directly invoking the Miri driver, this variable (or a
@@ -568,6 +570,15 @@ extern "Rust" {
     /// program) the contents of a section of program memory, as bytes. Bytes
     /// written using this function will emerge from the interpreter's stderr.
     fn miri_write_to_stderr(bytes: &[u8]);
+
+    /// Miri-provided extern function to allocate memory from the interpreter.
+    /// 
+    /// This is useful when no fundamental way of allocating memory is
+    /// available, e.g. when using `no_std` + `alloc`.
+    fn miri_alloc(size: usize, align: usize) -> *mut u8;
+
+    /// Miri-provided extern function to deallocate memory.
+    fn miri_dealloc(ptr: *mut u8, size: usize, align: usize);
 }
 ```
 
diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock
index 2beb6e1f1a4..3e5d388668b 100644
--- a/src/tools/miri/cargo-miri/Cargo.lock
+++ b/src/tools/miri/cargo-miri/Cargo.lock
@@ -175,9 +175,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-build-sysroot"
-version = "0.3.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec5f3689b6c560d6a3a17fcbe54204cd870b4fcf46342d60de16715b660d2c92"
+checksum = "20c4b4625eeb148cccf82d5e9b90ad7fab3b11a0204cf75cc7fa04981a0fdffd"
 dependencies = [
  "anyhow",
  "rustc_version",
diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml
index fcdd122747d..ce8457469e7 100644
--- a/src/tools/miri/cargo-miri/Cargo.toml
+++ b/src/tools/miri/cargo-miri/Cargo.toml
@@ -18,7 +18,7 @@ directories = "4"
 rustc_version = "0.4"
 serde_json = "1.0.40"
 cargo_metadata = "0.15.0"
-rustc-build-sysroot = "0.3.3"
+rustc-build-sysroot = "0.4"
 
 # A noop dependency that changes in the Rust repository, it's a bit of a hack.
 # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs
index f3841a61408..9c179e82ba1 100644
--- a/src/tools/miri/cargo-miri/src/setup.rs
+++ b/src/tools/miri/cargo-miri/src/setup.rs
@@ -5,7 +5,7 @@ use std::ffi::OsStr;
 use std::path::PathBuf;
 use std::process::{self, Command};
 
-use rustc_build_sysroot::{BuildMode, Sysroot, SysrootConfig};
+use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig};
 use rustc_version::VersionMeta;
 
 use crate::util::*;
@@ -70,9 +70,11 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
     let sysroot_config = if std::env::var_os("MIRI_NO_STD").is_some() {
         SysrootConfig::NoStd
     } else {
-        SysrootConfig::WithStd { std_features: &["panic_unwind", "backtrace"] }
+        SysrootConfig::WithStd {
+            std_features: ["panic_unwind", "backtrace"].into_iter().map(Into::into).collect(),
+        }
     };
-    let cargo_cmd = || {
+    let cargo_cmd = {
         let mut command = cargo();
         // Use Miri as rustc to build a libstd compatible with us (and use the right flags).
         // However, when we are running in bootstrap, we cannot just overwrite `RUSTC`,
@@ -106,13 +108,14 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
             command.stdout(process::Stdio::null());
             command.stderr(process::Stdio::null());
         }
-        // Disable debug assertions in the standard library -- Miri is already slow enough.
-        // But keep the overflow checks, they are cheap. This completely overwrites flags
-        // the user might have set, which is consistent with normal `cargo build` that does
-        // not apply `RUSTFLAGS` to the sysroot either.
-        let rustflags = vec!["-Cdebug-assertions=off".into(), "-Coverflow-checks=on".into()];
-        (command, rustflags)
+
+        command
     };
+    // Disable debug assertions in the standard library -- Miri is already slow enough.
+    // But keep the overflow checks, they are cheap. This completely overwrites flags
+    // the user might have set, which is consistent with normal `cargo build` that does
+    // not apply `RUSTFLAGS` to the sysroot either.
+    let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"];
     // Make sure all target-level Miri invocations know their sysroot.
     std::env::set_var("MIRI_SYSROOT", &sysroot_dir);
 
@@ -124,8 +127,13 @@ pub fn setup(subcommand: &MiriCommand, target: &str, rustc_version: &VersionMeta
         // We want to be quiet, but still let the user know that something is happening.
         eprint!("Preparing a sysroot for Miri (target: {target})... ");
     }
-    Sysroot::new(&sysroot_dir, target)
-        .build_from_source(&rust_src, BuildMode::Check, sysroot_config, rustc_version, cargo_cmd)
+    SysrootBuilder::new(&sysroot_dir, target)
+        .build_mode(BuildMode::Check)
+        .rustc_version(rustc_version.clone())
+        .sysroot_config(sysroot_config)
+        .rustflags(rustflags)
+        .cargo(cargo_cmd)
+        .build_from_source(&rust_src)
         .unwrap_or_else(|_| {
             if only_setup {
                 show_error!("failed to build sysroot, see error details above")
diff --git a/src/tools/miri/miri b/src/tools/miri/miri
index f0986bfb1cd..b09897c294c 100755
--- a/src/tools/miri/miri
+++ b/src/tools/miri/miri
@@ -42,21 +42,21 @@ many different seeds.
 Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
 <benches> can explicitly list the benchmarks to run; by default, all of them are run.
 
+./miri toolchain <flags>:
+Update and activate the rustup toolchain 'miri' to the commit given in the
+`rust-version` file.
+`rustup-toolchain-install-master` must be installed for this to work. Any extra
+flags are passed to `rustup-toolchain-install-master`.
+
 ./miri rustc-pull:
-Pull and merge Miri changes from the rustc repo.
+Pull and merge Miri changes from the rustc repo. The fetched commit is stored in
+the `rust-version` file, so the next `./miri toolchain` will install the rustc
+we just pulled.
 
 ./miri rustc-push <github user> <branch>:
-Push Miri changes back to the rustc repo. This will update the 'master' branch
-in the Rust fork of the given user to upstream. It will also pull a copy of the
-rustc history into the Miri repo, unless you set the RUSTC_GIT env var to an
-existing clone of the rustc repo.
-
-./miri toolchain <commit> <flags>:
-Update and activate the rustup toolchain 'miri'. If no commit is given, updates
-to the commit given in the `rust-version` file. If the commit is `HEAD`, updates
-to the latest upstream rustc commit.
-`rustup-toolchain-install-master` must be installed for this to work. Any extra
-flags are passed to `rustup-toolchain-install-master`.
+Push Miri changes back to the rustc repo. This will pull a copy of the rustc
+history into the Miri repo, unless you set the RUSTC_GIT env var to an existing
+clone of the rustc repo.
 
   ENVIRONMENT VARIABLES
 
@@ -78,7 +78,7 @@ shift
 # macOS does not have a useful readlink/realpath so we have to use Python instead...
 MIRIDIR=$(python3 -c 'import os, sys; print(os.path.dirname(os.path.realpath(sys.argv[1])))' "$0")
 # Used for rustc syncs.
-JOSH_FILTER=":at_commit=75dd959a3a40eb5b4574f8d2e23aa6efbeb33573[:prefix=src/tools/miri]:/src/tools/miri"
+JOSH_FILTER=":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"
 # Needed for `./miri bench`.
 TOOLCHAIN=$(cd "$MIRIDIR"; rustup show active-toolchain | head -n 1 | cut -d ' ' -f 1)
 
@@ -86,21 +86,12 @@ TOOLCHAIN=$(cd "$MIRIDIR"; rustup show active-toolchain | head -n 1 | cut -d ' '
 case "$COMMAND" in
 toolchain)
     cd "$MIRIDIR"
+    NEW_COMMIT=$(cat rust-version)
     # Make sure rustup-toolchain-install-master is installed.
     if ! which rustup-toolchain-install-master >/dev/null; then
         echo "Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'"
         exit 1
     fi
-    # Determine new commit.
-    if [[ "$1" == "" ]]; then
-        NEW_COMMIT=$(cat rust-version)
-    elif [[ "$1" == "HEAD" ]]; then
-        NEW_COMMIT=$(git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1)
-    else
-        NEW_COMMIT="$1"
-    fi
-    echo "$NEW_COMMIT" > rust-version
-    shift || true # don't fail if shifting fails because no commit was given
     # Check if we already are at that commit.
     CUR_COMMIT=$(rustc +miri --version -v 2>/dev/null | grep "^commit-hash: " | cut -d " " -f 2)
     if [[ "$CUR_COMMIT" == "$NEW_COMMIT" ]]; then
@@ -122,8 +113,18 @@ toolchain)
     ;;
 rustc-pull)
     cd "$MIRIDIR"
+    FETCH_COMMIT=$(git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1)
+    # We can't pull from a commit with josh
+    # (https://github.com/josh-project/josh/issues/1034), so we just hope that
+    # nothing gets merged into rustc *during* this pull.
     git fetch http://localhost:8000/rust-lang/rust.git$JOSH_FILTER.git master
+    # Just verify that `master` didn't move.
+    if [[ $FETCH_COMMIT != $(git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1) ]]; then
+        echo "Looks like something got merged into Rust *while we were pulling*. Aborting. Please try again."
+    fi
+    echo "$FETCH_COMMIT" > rust-version # do this *before* merging as merging will fail in case of conflicts
     git merge FETCH_HEAD --no-ff -m "Merge from rustc"
+    git commit rust-version --amend -m "Merge from rustc"
     exit 0
     ;;
 rustc-push)
@@ -145,19 +146,21 @@ rustc-push)
         fi
         cd "$MIRIDIR"
     fi
-    # Prepare the branches. For reliable pushing we need to push to a non-existent branch
-    # and set `-o base` to a branch that holds current rustc master.
-    echo "Preparing $USER/rust..."
-    if git fetch https://github.com/$USER/rust $BRANCH &>/dev/null; then
-        echo "The '$BRANCH' seems to already exist in $USER/rust. Please delete it and try again."
+    # Prepare the branch. Pushing works much better if we use as base exactly
+    # the commit that we pulled from last time, so we use the `rust-version`
+    # file as a good approximation of that.
+    BASE=$(cat "$MIRIDIR/rust-version")
+    echo "Preparing $USER/rust (base: $BASE)..."
+    if git fetch "https://github.com/$USER/rust" "$BRANCH" &>/dev/null; then
+        echo "The branch '$BRANCH' seems to already exist in 'https://github.com/$USER/rust'. Please delete it and try again."
         exit 1
     fi
-    git fetch https://github.com/rust-lang/rust master
-    git push https://github.com/$USER/rust FETCH_HEAD:master
+    git fetch https://github.com/rust-lang/rust $BASE
+    git push https://github.com/$USER/rust $BASE:refs/heads/$BRANCH -f
     # Do the actual push.
     cd "$MIRIDIR"
     echo "Pushing Miri changes..."
-    git push http://localhost:8000/$USER/rust.git$JOSH_FILTER.git HEAD:$BRANCH -o base=master
+    git push http://localhost:8000/$USER/rust.git$JOSH_FILTER.git HEAD:$BRANCH
     exit 0
     ;;
 many-seeds)
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 13492d183c9..746fcb2e183 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-b03502b35d111bef0399a66ab3cc765f0802e8ba
+101e1822c3e54e63996c8aaa014d55716f3937eb
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index d0fc349f1ac..b0f76646212 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -49,7 +49,7 @@ use std::{
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_middle::{mir, ty::layout::TyAndLayout};
+use rustc_middle::mir;
 use rustc_target::abi::{Align, Size};
 
 use crate::*;
@@ -440,33 +440,6 @@ impl MemoryCellClocks {
 /// Evaluation context extensions.
 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {}
 pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
-    /// Atomic variant of read_scalar_at_offset.
-    fn read_scalar_at_offset_atomic(
-        &self,
-        op: &OpTy<'tcx, Provenance>,
-        offset: u64,
-        layout: TyAndLayout<'tcx>,
-        atomic: AtomicReadOrd,
-    ) -> InterpResult<'tcx, Scalar<Provenance>> {
-        let this = self.eval_context_ref();
-        let value_place = this.deref_operand_and_offset(op, offset, layout)?;
-        this.read_scalar_atomic(&value_place, atomic)
-    }
-
-    /// Atomic variant of write_scalar_at_offset.
-    fn write_scalar_at_offset_atomic(
-        &mut self,
-        op: &OpTy<'tcx, Provenance>,
-        offset: u64,
-        value: impl Into<Scalar<Provenance>>,
-        layout: TyAndLayout<'tcx>,
-        atomic: AtomicWriteOrd,
-    ) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-        let value_place = this.deref_operand_and_offset(op, offset, layout)?;
-        this.write_scalar_atomic(value.into(), &value_place, atomic)
-    }
-
     /// Perform an atomic read operation at the memory location.
     fn read_scalar_atomic(
         &self,
@@ -713,7 +686,10 @@ impl VClockAlloc {
         let (alloc_timestamp, alloc_index) = match kind {
             // User allocated and stack memory should track allocation.
             MemoryKind::Machine(
-                MiriMemoryKind::Rust | MiriMemoryKind::C | MiriMemoryKind::WinHeap,
+                MiriMemoryKind::Rust
+                | MiriMemoryKind::Miri
+                | MiriMemoryKind::C
+                | MiriMemoryKind::WinHeap,
             )
             | MemoryKind::Stack => {
                 let (alloc_index, clocks) = global.current_thread_state(thread_mgr);
diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs
index ba5ae852c5a..dc4b435b710 100644
--- a/src/tools/miri/src/concurrency/sync.rs
+++ b/src/tools/miri/src/concurrency/sync.rs
@@ -193,8 +193,9 @@ impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'m
 pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>:
     crate::MiriInterpCxExt<'mir, 'tcx>
 {
+    /// Lazily initialize the ID of this Miri sync structure.
+    /// ('0' indicates uninit.)
     #[inline]
-    // Miri sync structures contain zero-initialized ids stored at some offset behind a pointer
     fn get_or_create_id<Id: SyncId>(
         &mut self,
         next_id: Id,
@@ -205,6 +206,7 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>:
         let value_place =
             this.deref_operand_and_offset(lock_op, offset, this.machine.layouts.u32)?;
 
+        // Since we are lazy, this update has to be atomic.
         let (old, success) = this
             .atomic_compare_exchange_scalar(
                 &value_place,
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index ac5dcbf0f4f..8fbee9a3522 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -712,7 +712,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             if tcx.is_foreign_item(def_id) {
                 throw_unsup_format!("foreign thread-local statics are not supported");
             }
-            let allocation = tcx.eval_static_initializer(def_id)?;
+            // We don't give a span -- statics don't need that, they cannot be generic or associated.
+            let allocation = this.ctfe_query(None, |tcx| tcx.eval_static_initializer(def_id))?;
             let mut allocation = allocation.inner().clone();
             // This allocation will be deallocated when the thread dies, so it is not in read-only memory.
             allocation.mutability = Mutability::Mut;
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index ec81ffd3cd5..7658cea10f9 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -146,7 +146,9 @@ fn prune_stacktrace<'tcx>(
     }
 }
 
-/// Emit a custom diagnostic without going through the miri-engine machinery
+/// Emit a custom diagnostic without going through the miri-engine machinery.
+///
+/// Returns `Some` if this was regular program termination with a given exit code, `None` otherwise.
 pub fn report_error<'tcx, 'mir>(
     ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
     e: InterpErrorInfo<'tcx>,
@@ -155,106 +157,102 @@ pub fn report_error<'tcx, 'mir>(
 
     let mut msg = vec![];
 
-    let (title, helps) = match &e.kind() {
-        MachineStop(info) => {
-            let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
-            use TerminationInfo::*;
-            let title = match info {
-                Exit(code) => return Some(*code),
-                Abort(_) => Some("abnormal termination"),
-                UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance =>
-                    Some("unsupported operation"),
-                StackedBorrowsUb { .. } => Some("Undefined Behavior"),
-                Deadlock => Some("deadlock"),
-                MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
-            };
-            #[rustfmt::skip]
-            let helps = match info {
-                UnsupportedInIsolation(_) =>
-                    vec![
-                        (None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")),
-                        (None, format!("or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning")),
-                    ],
-                StackedBorrowsUb { help, history, .. } => {
-                    let url = "https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md";
-                    msg.extend(help.clone());
-                    let mut helps = vec![
-                        (None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental")),
-                        (None, format!("see {url} for further information")),
-                    ];
-                    if let Some(TagHistory {created, invalidated, protected}) = history.clone() {
-                        helps.push((Some(created.1), created.0));
-                        if let Some((msg, span)) = invalidated {
-                            helps.push((Some(span), msg));
-                        }
-                        if let Some((protector_msg, protector_span)) = protected {
-                            helps.push((Some(protector_span), protector_msg));
-                        }
+    let (title, helps) = if let MachineStop(info) = e.kind() {
+        let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
+        use TerminationInfo::*;
+        let title = match info {
+            Exit(code) => return Some(*code),
+            Abort(_) => Some("abnormal termination"),
+            UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance =>
+                Some("unsupported operation"),
+            StackedBorrowsUb { .. } => Some("Undefined Behavior"),
+            Deadlock => Some("deadlock"),
+            MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
+        };
+        #[rustfmt::skip]
+        let helps = match info {
+            UnsupportedInIsolation(_) =>
+                vec![
+                    (None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")),
+                    (None, format!("or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning")),
+                ],
+            StackedBorrowsUb { help, history, .. } => {
+                let url = "https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md";
+                msg.extend(help.clone());
+                let mut helps = vec![
+                    (None, format!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental")),
+                    (None, format!("see {url} for further information")),
+                ];
+                if let Some(TagHistory {created, invalidated, protected}) = history.clone() {
+                    helps.push((Some(created.1), created.0));
+                    if let Some((msg, span)) = invalidated {
+                        helps.push((Some(span), msg));
+                    }
+                    if let Some((protector_msg, protector_span)) = protected {
+                        helps.push((Some(protector_span), protector_msg));
                     }
-                    helps
                 }
-                MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
-                    vec![
-                        (Some(*first), format!("it's first defined here, in crate `{first_crate}`")),
-                        (Some(*second), format!("then it's defined here again, in crate `{second_crate}`")),
-                    ],
-                SymbolShimClashing { link_name, span } =>
-                    vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))],
-                Int2PtrWithStrictProvenance =>
-                    vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))],
-                _ => vec![],
-            };
-            (title, helps)
-        }
-        _ => {
-            #[rustfmt::skip]
-            let title = match e.kind() {
-                Unsupported(_) =>
-                    "unsupported operation",
-                UndefinedBehavior(_) =>
-                    "Undefined Behavior",
-                ResourceExhaustion(_) =>
-                    "resource exhaustion",
-                InvalidProgram(
-                    InvalidProgramInfo::AlreadyReported(_) |
-                    InvalidProgramInfo::Layout(..) |
-                    InvalidProgramInfo::ReferencedConstant
-                ) =>
-                    "post-monomorphization error",
-                kind =>
-                    bug!("This error should be impossible in Miri: {kind:?}"),
-            };
-            #[rustfmt::skip]
-            let helps = match e.kind() {
-                Unsupported(
-                    UnsupportedOpInfo::ThreadLocalStatic(_) |
-                    UnsupportedOpInfo::ReadExternStatic(_) |
-                    UnsupportedOpInfo::PartialPointerOverwrite(_) | // we make memory uninit instead
-                    UnsupportedOpInfo::ReadPointerAsBytes
-                ) =>
-                    panic!("Error should never be raised by Miri: {kind:?}", kind = e.kind()),
-                Unsupported(
-                    UnsupportedOpInfo::Unsupported(_) |
-                    UnsupportedOpInfo::PartialPointerCopy(_)
-                ) =>
-                    vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))],
-                UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. })
-                    if ecx.machine.check_alignment == AlignmentCheck::Symbolic
-                =>
-                    vec![
-                        (None, format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior")),
-                        (None, format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives")),
-                    ],
-                UndefinedBehavior(_) =>
-                    vec![
-                        (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
-                        (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
-                    ],
-                InvalidProgram(_) | ResourceExhaustion(_) | MachineStop(_) =>
-                    vec![],
-            };
-            (Some(title), helps)
-        }
+                helps
+            }
+            MultipleSymbolDefinitions { first, first_crate, second, second_crate, .. } =>
+                vec![
+                    (Some(*first), format!("it's first defined here, in crate `{first_crate}`")),
+                    (Some(*second), format!("then it's defined here again, in crate `{second_crate}`")),
+                ],
+            SymbolShimClashing { link_name, span } =>
+                vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))],
+            Int2PtrWithStrictProvenance =>
+                vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))],
+            _ => vec![],
+        };
+        (title, helps)
+    } else {
+        #[rustfmt::skip]
+        let title = match e.kind() {
+            UndefinedBehavior(_) =>
+                "Undefined Behavior",
+            ResourceExhaustion(_) =>
+                "resource exhaustion",
+            Unsupported(
+                // We list only the ones that can actually happen.
+                UnsupportedOpInfo::Unsupported(_)
+            ) =>
+                "unsupported operation",
+            InvalidProgram(
+                // We list only the ones that can actually happen.
+                InvalidProgramInfo::AlreadyReported(_) |
+                InvalidProgramInfo::Layout(..)
+            ) =>
+                "post-monomorphization error",
+            kind =>
+                bug!("This error should be impossible in Miri: {kind:?}"),
+        };
+        #[rustfmt::skip]
+        let helps = match e.kind() {
+            Unsupported(_) =>
+                vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))],
+            UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. })
+                if ecx.machine.check_alignment == AlignmentCheck::Symbolic
+            =>
+                vec![
+                    (None, format!("this usually indicates that your program performed an invalid operation and caused Undefined Behavior")),
+                    (None, format!("but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives")),
+                ],
+            UndefinedBehavior(_) =>
+                vec![
+                    (None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")),
+                    (None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")),
+                ],
+            InvalidProgram(
+                InvalidProgramInfo::AlreadyReported(rustc_errors::ErrorGuaranteed { .. })
+            ) => {
+                // This got already reported. No point in reporting it again.
+                return None;
+            }
+            _ =>
+                vec![],
+        };
+        (Some(title), helps)
     };
 
     let stacktrace = ecx.generate_stacktrace();
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index f98727186c4..cd5e989b434 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -117,7 +117,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let this = self.eval_context_ref();
         let instance = this.resolve_path(path);
         let cid = GlobalId { instance, promoted: None };
-        let const_val = this.eval_to_allocation(cid)?;
+        // We don't give a span -- this isn't actually used directly by the program anyway.
+        let const_val = this.eval_global(cid, None)?;
         this.read_scalar(&const_val.into())
     }
 
@@ -667,7 +668,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         layout: TyAndLayout<'tcx>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
         let this = self.eval_context_ref();
-        let op_place = this.deref_operand(op)?;
+        let op_place = this.deref_operand(op)?; // FIXME: we still deref with the original type!
         let offset = Size::from_bytes(offset);
 
         // Ensure that the access is within bounds.
@@ -687,17 +688,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         this.read_scalar(&value_place.into())
     }
 
-    fn write_immediate_at_offset(
-        &mut self,
-        op: &OpTy<'tcx, Provenance>,
-        offset: u64,
-        value: &ImmTy<'tcx, Provenance>,
-    ) -> InterpResult<'tcx, ()> {
-        let this = self.eval_context_mut();
-        let value_place = this.deref_operand_and_offset(op, offset, value.layout)?;
-        this.write_immediate(**value, &value_place.into())
-    }
-
     fn write_scalar_at_offset(
         &mut self,
         op: &OpTy<'tcx, Provenance>,
@@ -705,7 +695,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         value: impl Into<Scalar<Provenance>>,
         layout: TyAndLayout<'tcx>,
     ) -> InterpResult<'tcx, ()> {
-        self.write_immediate_at_offset(op, offset, &ImmTy::from_scalar(value.into(), layout))
+        let this = self.eval_context_mut();
+        let value_place = this.deref_operand_and_offset(op, offset, layout)?;
+        this.write_scalar(value, &value_place.into())
     }
 
     /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None`
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 8028ce75354..66df0d737c0 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -46,6 +46,7 @@ extern crate rustc_ast;
 extern crate rustc_middle;
 extern crate rustc_const_eval;
 extern crate rustc_data_structures;
+extern crate rustc_errors;
 extern crate rustc_hir;
 extern crate rustc_index;
 extern crate rustc_session;
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 231a99c1d03..8243ccd90a3 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -77,6 +77,8 @@ impl VisitTags for FrameData<'_> {
 pub enum MiriMemoryKind {
     /// `__rust_alloc` memory.
     Rust,
+    /// `miri_alloc` memory.
+    Miri,
     /// `malloc` memory.
     C,
     /// Windows `HeapAlloc` memory.
@@ -110,7 +112,7 @@ impl MayLeak for MiriMemoryKind {
     fn may_leak(self) -> bool {
         use self::MiriMemoryKind::*;
         match self {
-            Rust | C | WinHeap | Runtime => false,
+            Rust | Miri | C | WinHeap | Runtime => false,
             Machine | Global | ExternStatic | Tls => true,
         }
     }
@@ -121,6 +123,7 @@ impl fmt::Display for MiriMemoryKind {
         use self::MiriMemoryKind::*;
         match self {
             Rust => write!(f, "Rust heap"),
+            Miri => write!(f, "Miri bare-metal heap"),
             C => write!(f, "C heap"),
             WinHeap => write!(f, "Windows heap"),
             Machine => write!(f, "machine-managed memory"),
@@ -133,7 +136,7 @@ impl fmt::Display for MiriMemoryKind {
 }
 
 /// Pointer provenance.
-#[derive(Debug, Clone, Copy)]
+#[derive(Clone, Copy)]
 pub enum Provenance {
     Concrete {
         alloc_id: AllocId,
@@ -176,18 +179,9 @@ static_assert_size!(Pointer<Provenance>, 24);
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(Scalar<Provenance>, 32);
 
-impl interpret::Provenance for Provenance {
-    /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
-    const OFFSET_IS_ADDR: bool = true;
-
-    /// We cannot err on partial overwrites, it happens too often in practice (due to unions).
-    const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = false;
-
-    fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let (prov, addr) = ptr.into_parts(); // address is absolute
-        write!(f, "{:#x}", addr.bytes())?;
-
-        match prov {
+impl fmt::Debug for Provenance {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
             Provenance::Concrete { alloc_id, sb } => {
                 // Forward `alternate` flag to `alloc_id` printing.
                 if f.alternate() {
@@ -202,9 +196,13 @@ impl interpret::Provenance for Provenance {
                 write!(f, "[wildcard]")?;
             }
         }
-
         Ok(())
     }
+}
+
+impl interpret::Provenance for Provenance {
+    /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
+    const OFFSET_IS_ADDR: bool = true;
 
     fn get_alloc_id(self) -> Option<AllocId> {
         match self {
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 1b3205aabc9..058f730833b 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -513,22 +513,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             }
 
             // Rust allocation
-            "__rust_alloc" => {
+            "__rust_alloc" | "miri_alloc" => {
                 let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
 
-                return this.emulate_allocator(Symbol::intern("__rg_alloc"), |this| {
+                let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
                     Self::check_alloc_request(size, align)?;
 
+                    let memory_kind = match link_name.as_str() {
+                        "__rust_alloc" => MiriMemoryKind::Rust,
+                        "miri_alloc" => MiriMemoryKind::Miri,
+                        _ => unreachable!(),
+                    };
+
                     let ptr = this.allocate_ptr(
                         Size::from_bytes(size),
                         Align::from_bytes(align).unwrap(),
-                        MiriMemoryKind::Rust.into(),
+                        memory_kind.into(),
                     )?;
 
                     this.write_pointer(ptr, dest)
-                });
+                };
+
+                match link_name.as_str() {
+                    "__rust_alloc" => return this.emulate_allocator(Symbol::intern("__rg_alloc"), default),
+                    "miri_alloc" => {
+                        default(this)?;
+                        return Ok(EmulateByNameResult::NeedsJumping);
+                    },
+                    _ => unreachable!(),
+                }
             }
             "__rust_alloc_zeroed" => {
                 let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
@@ -549,20 +564,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     this.write_pointer(ptr, dest)
                 });
             }
-            "__rust_dealloc" => {
+            "__rust_dealloc" | "miri_dealloc" => {
                 let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
 
-                return this.emulate_allocator(Symbol::intern("__rg_dealloc"), |this| {
+                let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
+                    let memory_kind = match link_name.as_str() {
+                        "__rust_dealloc" => MiriMemoryKind::Rust,
+                        "miri_dealloc" => MiriMemoryKind::Miri,
+                        _ => unreachable!(),
+                    };
+
                     // No need to check old_size/align; we anyway check that they match the allocation.
                     this.deallocate_ptr(
                         ptr,
                         Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
-                        MiriMemoryKind::Rust.into(),
+                        memory_kind.into(),
                     )
-                });
+                };
+
+                match link_name.as_str() {
+                    "__rust_dealloc" => return this.emulate_allocator(Symbol::intern("__rg_dealloc"), default),
+                    "miri_dealloc" => {
+                        default(this)?;
+                        return Ok(EmulateByNameResult::NeedsJumping);
+                    }
+                    _ => unreachable!(),
+                }
             }
             "__rust_realloc" => {
                 let [ptr, old_size, align, new_size] = this.check_shim(abi, Abi::Rust, link_name, args)?;
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index a7275646847..e0afb500cb1 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -1,8 +1,5 @@
 use std::time::SystemTime;
 
-use rustc_hir::LangItem;
-use rustc_middle::ty::{layout::TyAndLayout, query::TyCtxtAt, Ty};
-
 use crate::concurrency::sync::CondvarLock;
 use crate::concurrency::thread::{MachineCallback, Time};
 use crate::*;
@@ -20,39 +17,34 @@ use crate::*;
 /// in `pthread_mutexattr_settype` function.
 const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000;
 
-const MUTEX_ID_OFFSET: u64 = 4;
-const RWLOCK_ID_OFFSET: u64 = 4;
-const CONDVAR_ID_OFFSET: u64 = 4;
-
 fn is_mutex_kind_default<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
-    kind: Scalar<Provenance>,
+    kind: i32,
 ) -> InterpResult<'tcx, bool> {
-    Ok(kind == ecx.eval_libc("PTHREAD_MUTEX_DEFAULT")?)
+    Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")?)
 }
 
 fn is_mutex_kind_normal<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
-    kind: Scalar<Provenance>,
+    kind: i32,
 ) -> InterpResult<'tcx, bool> {
-    let kind = kind.to_i32()?;
-    let mutex_normal_kind = ecx.eval_libc("PTHREAD_MUTEX_NORMAL")?.to_i32()?;
+    let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")?;
     Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG))
 }
 
 fn mutexattr_get_kind<'mir, 'tcx: 'mir>(
     ecx: &MiriInterpCx<'mir, 'tcx>,
     attr_op: &OpTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32)
+) -> InterpResult<'tcx, i32> {
+    ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32)?.to_i32()
 }
 
 fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     attr_op: &OpTy<'tcx, Provenance>,
-    kind: impl Into<Scalar<Provenance>>,
+    kind: i32,
 ) -> InterpResult<'tcx, ()> {
-    ecx.write_scalar_at_offset(attr_op, 0, kind, layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32))
+    ecx.write_scalar_at_offset(attr_op, 0, Scalar::from_i32(kind), ecx.machine.layouts.i32)
 }
 
 // pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
@@ -64,53 +56,35 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
 // bytes 12-15 or 16-19 (depending on platform): mutex kind, as an i32
 // (the kind has to be at its offset for compatibility with static initializer macros)
 
-fn mutex_get_kind<'mir, 'tcx: 'mir>(
-    ecx: &MiriInterpCx<'mir, 'tcx>,
+fn mutex_get_id<'mir, 'tcx: 'mir>(
+    ecx: &mut MiriInterpCx<'mir, 'tcx>,
     mutex_op: &OpTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
-    ecx.read_scalar_at_offset_atomic(
-        mutex_op,
-        offset,
-        ecx.machine.layouts.i32,
-        AtomicReadOrd::Relaxed,
-    )
+) -> InterpResult<'tcx, MutexId> {
+    ecx.mutex_get_or_create_id(mutex_op, 4)
 }
 
-fn mutex_set_kind<'mir, 'tcx: 'mir>(
+fn mutex_reset_id<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     mutex_op: &OpTy<'tcx, Provenance>,
-    kind: impl Into<Scalar<Provenance>>,
 ) -> InterpResult<'tcx, ()> {
-    let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
-    ecx.write_scalar_at_offset_atomic(
-        mutex_op,
-        offset,
-        kind,
-        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32),
-        AtomicWriteOrd::Relaxed,
-    )
+    ecx.write_scalar_at_offset(mutex_op, 4, Scalar::from_i32(0), ecx.machine.layouts.u32)
 }
 
-fn mutex_get_id<'mir, 'tcx: 'mir>(
+fn mutex_get_kind<'mir, 'tcx: 'mir>(
     ecx: &MiriInterpCx<'mir, 'tcx>,
     mutex_op: &OpTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    ecx.read_scalar_at_offset_atomic(mutex_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed)
+) -> InterpResult<'tcx, i32> {
+    let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
+    ecx.read_scalar_at_offset(mutex_op, offset, ecx.machine.layouts.i32)?.to_i32()
 }
 
-fn mutex_set_id<'mir, 'tcx: 'mir>(
+fn mutex_set_kind<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     mutex_op: &OpTy<'tcx, Provenance>,
-    id: impl Into<Scalar<Provenance>>,
+    kind: i32,
 ) -> InterpResult<'tcx, ()> {
-    ecx.write_scalar_at_offset_atomic(
-        mutex_op,
-        4,
-        id,
-        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
-        AtomicWriteOrd::Relaxed,
-    )
+    let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
+    ecx.write_scalar_at_offset(mutex_op, offset, Scalar::from_i32(kind), ecx.machine.layouts.i32)
 }
 
 // pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
@@ -121,10 +95,10 @@ fn mutex_set_id<'mir, 'tcx: 'mir>(
 // bytes 4-7: rwlock id as u32 or 0 if id is not assigned yet.
 
 fn rwlock_get_id<'mir, 'tcx: 'mir>(
-    ecx: &MiriInterpCx<'mir, 'tcx>,
+    ecx: &mut MiriInterpCx<'mir, 'tcx>,
     rwlock_op: &OpTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    ecx.read_scalar_at_offset_atomic(rwlock_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed)
+) -> InterpResult<'tcx, RwLockId> {
+    ecx.rwlock_get_or_create_id(rwlock_op, 4)
 }
 
 // pthread_condattr_t
@@ -136,21 +110,16 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>(
 fn condattr_get_clock_id<'mir, 'tcx: 'mir>(
     ecx: &MiriInterpCx<'mir, 'tcx>,
     attr_op: &OpTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32)
+) -> InterpResult<'tcx, i32> {
+    ecx.read_scalar_at_offset(attr_op, 0, ecx.machine.layouts.i32)?.to_i32()
 }
 
 fn condattr_set_clock_id<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     attr_op: &OpTy<'tcx, Provenance>,
-    clock_id: impl Into<Scalar<Provenance>>,
+    clock_id: i32,
 ) -> InterpResult<'tcx, ()> {
-    ecx.write_scalar_at_offset(
-        attr_op,
-        0,
-        clock_id,
-        layout_of_maybe_uninit(ecx.tcx, ecx.machine.layouts.i32.ty),
-    )
+    ecx.write_scalar_at_offset(attr_op, 0, Scalar::from_i32(clock_id), ecx.machine.layouts.i32)
 }
 
 // pthread_cond_t
@@ -163,44 +132,32 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>(
 // bytes 8-11: the clock id constant as i32
 
 fn cond_get_id<'mir, 'tcx: 'mir>(
-    ecx: &MiriInterpCx<'mir, 'tcx>,
+    ecx: &mut MiriInterpCx<'mir, 'tcx>,
     cond_op: &OpTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    ecx.read_scalar_at_offset_atomic(cond_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed)
+) -> InterpResult<'tcx, CondvarId> {
+    ecx.condvar_get_or_create_id(cond_op, 4)
 }
 
-fn cond_set_id<'mir, 'tcx: 'mir>(
+fn cond_reset_id<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     cond_op: &OpTy<'tcx, Provenance>,
-    id: impl Into<Scalar<Provenance>>,
 ) -> InterpResult<'tcx, ()> {
-    ecx.write_scalar_at_offset_atomic(
-        cond_op,
-        4,
-        id,
-        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
-        AtomicWriteOrd::Relaxed,
-    )
+    ecx.write_scalar_at_offset(cond_op, 4, Scalar::from_i32(0), ecx.machine.layouts.u32)
 }
 
 fn cond_get_clock_id<'mir, 'tcx: 'mir>(
     ecx: &MiriInterpCx<'mir, 'tcx>,
     cond_op: &OpTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    ecx.read_scalar_at_offset(cond_op, 8, ecx.machine.layouts.i32)
+) -> InterpResult<'tcx, i32> {
+    ecx.read_scalar_at_offset(cond_op, 8, ecx.machine.layouts.i32)?.to_i32()
 }
 
 fn cond_set_clock_id<'mir, 'tcx: 'mir>(
     ecx: &mut MiriInterpCx<'mir, 'tcx>,
     cond_op: &OpTy<'tcx, Provenance>,
-    clock_id: impl Into<Scalar<Provenance>>,
+    clock_id: i32,
 ) -> InterpResult<'tcx, ()> {
-    ecx.write_scalar_at_offset(
-        cond_op,
-        8,
-        clock_id,
-        layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32),
-    )
+    ecx.write_scalar_at_offset(cond_op, 8, Scalar::from_i32(clock_id), ecx.machine.layouts.i32)
 }
 
 /// Try to reacquire the mutex associated with the condition variable after we
@@ -260,7 +217,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let default_kind = this.eval_libc("PTHREAD_MUTEX_DEFAULT")?;
+        let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")?;
         mutexattr_set_kind(this, attr_op, default_kind)?;
 
         Ok(0)
@@ -273,8 +230,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let kind = this.read_scalar(kind_op)?;
-        if kind == this.eval_libc("PTHREAD_MUTEX_NORMAL")? {
+        let kind = this.read_scalar(kind_op)?.to_i32()?;
+        if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL")? {
             // In `glibc` implementation, the numeric values of
             // `PTHREAD_MUTEX_NORMAL` and `PTHREAD_MUTEX_DEFAULT` are equal.
             // However, a mutex created by explicitly passing
@@ -287,16 +244,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             // use the same trick as glibc: for the case when
             // `pthread_mutexattr_settype` is caled explicitly, we set the
             // `PTHREAD_MUTEX_NORMAL_FLAG` flag.
-            let normal_kind = kind.to_i32()? | PTHREAD_MUTEX_NORMAL_FLAG;
+            let normal_kind = kind | PTHREAD_MUTEX_NORMAL_FLAG;
             // Check that after setting the flag, the kind is distinguishable
             // from all other kinds.
-            assert_ne!(normal_kind, this.eval_libc("PTHREAD_MUTEX_DEFAULT")?.to_i32()?);
-            assert_ne!(normal_kind, this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?.to_i32()?);
-            assert_ne!(normal_kind, this.eval_libc("PTHREAD_MUTEX_RECURSIVE")?.to_i32()?);
-            mutexattr_set_kind(this, attr_op, Scalar::from_i32(normal_kind))?;
-        } else if kind == this.eval_libc("PTHREAD_MUTEX_DEFAULT")?
-            || kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?
-            || kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")?
+            assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")?);
+            assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")?);
+            assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")?);
+            mutexattr_set_kind(this, attr_op, normal_kind)?;
+        } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")?
+            || kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")?
+            || kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")?
         {
             mutexattr_set_kind(this, attr_op, kind)?;
         } else {
@@ -342,13 +299,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
         let attr = this.read_pointer(attr_op)?;
         let kind = if this.ptr_is_null(attr)? {
-            this.eval_libc("PTHREAD_MUTEX_DEFAULT")?
+            this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")?
         } else {
             mutexattr_get_kind(this, attr_op)?
         };
 
         // Write 0 to use the same code path as the static initializers.
-        mutex_set_id(this, mutex_op, Scalar::from_i32(0))?;
+        mutex_reset_id(this, mutex_op)?;
 
         mutex_set_kind(this, mutex_op, kind)?;
 
@@ -359,7 +316,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let this = self.eval_context_mut();
 
         let kind = mutex_get_kind(this, mutex_op)?;
-        let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
+        let id = mutex_get_id(this, mutex_op)?;
         let active_thread = this.get_active_thread();
 
         if this.mutex_is_locked(id) {
@@ -374,9 +331,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     throw_ub_format!("trying to acquire already locked default mutex");
                 } else if is_mutex_kind_normal(this, kind)? {
                     throw_machine_stop!(TerminationInfo::Deadlock);
-                } else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")? {
+                } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")? {
                     this.eval_libc_i32("EDEADLK")
-                } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? {
+                } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")? {
                     this.mutex_lock(id, active_thread);
                     Ok(0)
                 } else {
@@ -399,7 +356,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let this = self.eval_context_mut();
 
         let kind = mutex_get_kind(this, mutex_op)?;
-        let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
+        let id = mutex_get_id(this, mutex_op)?;
         let active_thread = this.get_active_thread();
 
         if this.mutex_is_locked(id) {
@@ -409,10 +366,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             } else {
                 if is_mutex_kind_default(this, kind)?
                     || is_mutex_kind_normal(this, kind)?
-                    || kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?
+                    || kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")?
                 {
                     this.eval_libc_i32("EBUSY")
-                } else if kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")? {
+                } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")? {
                     this.mutex_lock(id, active_thread);
                     Ok(0)
                 } else {
@@ -435,7 +392,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let this = self.eval_context_mut();
 
         let kind = mutex_get_kind(this, mutex_op)?;
-        let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
+        let id = mutex_get_id(this, mutex_op)?;
         let active_thread = this.get_active_thread();
 
         if let Some(_old_locked_count) = this.mutex_unlock(id, active_thread) {
@@ -453,8 +410,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 throw_ub_format!(
                     "unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
                 );
-            } else if kind == this.eval_libc("PTHREAD_MUTEX_ERRORCHECK")?
-                || kind == this.eval_libc("PTHREAD_MUTEX_RECURSIVE")?
+            } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")?
+                || kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")?
             {
                 this.eval_libc_i32("EPERM")
             } else {
@@ -469,7 +426,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
+        let id = mutex_get_id(this, mutex_op)?;
 
         if this.mutex_is_locked(id) {
             throw_ub_format!("destroyed a locked mutex");
@@ -492,7 +449,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?;
+        let id = rwlock_get_id(this, rwlock_op)?;
         let active_thread = this.get_active_thread();
 
         if this.rwlock_is_write_locked(id) {
@@ -510,7 +467,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?;
+        let id = rwlock_get_id(this, rwlock_op)?;
         let active_thread = this.get_active_thread();
 
         if this.rwlock_is_write_locked(id) {
@@ -527,7 +484,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?;
+        let id = rwlock_get_id(this, rwlock_op)?;
         let active_thread = this.get_active_thread();
 
         if this.rwlock_is_locked(id) {
@@ -557,7 +514,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?;
+        let id = rwlock_get_id(this, rwlock_op)?;
         let active_thread = this.get_active_thread();
 
         if this.rwlock_is_locked(id) {
@@ -574,7 +531,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?;
+        let id = rwlock_get_id(this, rwlock_op)?;
         let active_thread = this.get_active_thread();
 
         #[allow(clippy::if_same_then_else)]
@@ -593,7 +550,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.rwlock_get_or_create_id(rwlock_op, RWLOCK_ID_OFFSET)?;
+        let id = rwlock_get_id(this, rwlock_op)?;
 
         if this.rwlock_is_locked(id) {
             throw_ub_format!("destroyed a locked rwlock");
@@ -618,7 +575,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // The default value of the clock attribute shall refer to the system
         // clock.
         // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html
-        let default_clock_id = this.eval_libc("CLOCK_REALTIME")?;
+        let default_clock_id = this.eval_libc_i32("CLOCK_REALTIME")?;
         condattr_set_clock_id(this, attr_op, default_clock_id)?;
 
         Ok(0)
@@ -631,9 +588,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, Scalar<Provenance>> {
         let this = self.eval_context_mut();
 
-        let clock_id = this.read_scalar(clock_id_op)?;
-        if clock_id == this.eval_libc("CLOCK_REALTIME")?
-            || clock_id == this.eval_libc("CLOCK_MONOTONIC")?
+        let clock_id = this.read_scalar(clock_id_op)?.to_i32()?;
+        if clock_id == this.eval_libc_i32("CLOCK_REALTIME")?
+            || clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")?
         {
             condattr_set_clock_id(this, attr_op, clock_id)?;
         } else {
@@ -652,7 +609,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let this = self.eval_context_mut();
 
         let clock_id = condattr_get_clock_id(this, attr_op)?;
-        this.write_scalar(clock_id, &this.deref_operand(clk_id_op)?.into())?;
+        this.write_scalar(Scalar::from_i32(clock_id), &this.deref_operand(clk_id_op)?.into())?;
 
         Ok(Scalar::from_i32(0))
     }
@@ -681,13 +638,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
         let attr = this.read_pointer(attr_op)?;
         let clock_id = if this.ptr_is_null(attr)? {
-            this.eval_libc("CLOCK_REALTIME")?
+            this.eval_libc_i32("CLOCK_REALTIME")?
         } else {
             condattr_get_clock_id(this, attr_op)?
         };
 
         // Write 0 to use the same code path as the static initializers.
-        cond_set_id(this, cond_op, Scalar::from_i32(0))?;
+        cond_reset_id(this, cond_op)?;
 
         cond_set_clock_id(this, cond_op, clock_id)?;
 
@@ -696,7 +653,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
     fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
-        let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
+        let id = cond_get_id(this, cond_op)?;
         if let Some((thread, lock)) = this.condvar_signal(id) {
             if let CondvarLock::Mutex(mutex) = lock {
                 post_cond_signal(this, thread, mutex)?;
@@ -713,7 +670,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         cond_op: &OpTy<'tcx, Provenance>,
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
-        let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
+        let id = cond_get_id(this, cond_op)?;
 
         while let Some((thread, lock)) = this.condvar_signal(id) {
             if let CondvarLock::Mutex(mutex) = lock {
@@ -733,8 +690,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
-        let mutex_id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
+        let id = cond_get_id(this, cond_op)?;
+        let mutex_id = mutex_get_id(this, mutex_op)?;
         let active_thread = this.get_active_thread();
 
         release_cond_mutex_and_block(this, active_thread, mutex_id)?;
@@ -752,12 +709,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
-        let mutex_id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
+        let id = cond_get_id(this, cond_op)?;
+        let mutex_id = mutex_get_id(this, mutex_op)?;
         let active_thread = this.get_active_thread();
 
         // Extract the timeout.
-        let clock_id = cond_get_clock_id(this, cond_op)?.to_i32()?;
+        let clock_id = cond_get_clock_id(this, cond_op)?;
         let duration = match this.read_timespec(&this.deref_operand(abstime_op)?)? {
             Some(duration) => duration,
             None => {
@@ -830,7 +787,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
+        let id = cond_get_id(this, cond_op)?;
         if this.condvar_is_awaited(id) {
             throw_ub_format!("destroying an awaited conditional variable");
         }
@@ -846,11 +803,3 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         Ok(0)
     }
 }
-
-fn layout_of_maybe_uninit<'tcx>(tcx: TyCtxtAt<'tcx>, param: Ty<'tcx>) -> TyAndLayout<'tcx> {
-    let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None);
-    let ty = tcx.bound_type_of(def_id).subst(*tcx, &[param.into()]);
-
-    let param_env = tcx.param_env(def_id);
-    tcx.layout_of(param_env.and(ty)).unwrap()
-}
diff --git a/src/tools/miri/src/stacked_borrows/mod.rs b/src/tools/miri/src/stacked_borrows/mod.rs
index 7f18e5dbae0..9ce02a02ec4 100644
--- a/src/tools/miri/src/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/stacked_borrows/mod.rs
@@ -1053,7 +1053,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 // pointers we need to retag, so we can stop recursion early.
                 // This optimization is crucial for ZSTs, because they can contain way more fields
                 // than we can ever visit.
-                if !place.layout.is_unsized() && place.layout.size < self.ecx.pointer_size() {
+                if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
                     return Ok(());
                 }
 
diff --git a/src/tools/miri/src/tag_gc.rs b/src/tools/miri/src/tag_gc.rs
index 5aa653632f3..73712348f0d 100644
--- a/src/tools/miri/src/tag_gc.rs
+++ b/src/tools/miri/src/tag_gc.rs
@@ -127,7 +127,7 @@ impl VisitTags for Operand<Provenance> {
 
 impl VisitTags for Allocation<Provenance, AllocExtra> {
     fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
-        for (_size, prov) in self.provenance().iter() {
+        for prov in self.provenance().provenances() {
             prov.visit_tags(visit);
         }
 
diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs
index 65fd7c2eccb..1d7ed34c59a 100644
--- a/src/tools/miri/tests/compiletest.rs
+++ b/src/tools/miri/tests/compiletest.rs
@@ -1,5 +1,5 @@
 use colored::*;
-use regex::Regex;
+use regex::bytes::Regex;
 use std::path::{Path, PathBuf};
 use std::{env, process::Command};
 use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling};
@@ -129,8 +129,8 @@ fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> R
 
 macro_rules! regexes {
     ($name:ident: $($regex:expr => $replacement:expr,)*) => {lazy_static::lazy_static! {
-        static ref $name: Vec<(Regex, &'static str)> = vec![
-            $((Regex::new($regex).unwrap(), $replacement),)*
+        static ref $name: Vec<(Regex, &'static [u8])> = vec![
+            $((Regex::new($regex).unwrap(), $replacement.as_bytes()),)*
         ];
     }};
 }
diff --git a/src/tools/miri/tests/fail/copy_half_a_pointer.rs b/src/tools/miri/tests/fail/copy_half_a_pointer.rs
deleted file mode 100644
index e1dcdda7fdf..00000000000
--- a/src/tools/miri/tests/fail/copy_half_a_pointer.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//@normalize-stderr-test: "\+0x[48]" -> "+HALF_PTR"
-#![allow(dead_code)]
-
-// We use packed structs to get around alignment restrictions
-#[repr(packed)]
-struct Data {
-    pad: u8,
-    ptr: &'static i32,
-}
-
-static G: i32 = 0;
-
-fn main() {
-    let mut d = Data { pad: 0, ptr: &G };
-
-    // Get a pointer to the beginning of the Data struct (one u8 byte, then the pointer bytes).
-    let d_alias = &mut d as *mut _ as *mut *const u8;
-    unsafe {
-        let _x = d_alias.read_unaligned(); //~ERROR: unable to copy parts of a pointer
-    }
-}
diff --git a/src/tools/miri/tests/fail/copy_half_a_pointer.stderr b/src/tools/miri/tests/fail/copy_half_a_pointer.stderr
deleted file mode 100644
index 21797757084..00000000000
--- a/src/tools/miri/tests/fail/copy_half_a_pointer.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: unsupported operation: unable to copy parts of a pointer from memory at ALLOC+HALF_PTR
-  --> $DIR/copy_half_a_pointer.rs:LL:CC
-   |
-LL |         let _x = d_alias.read_unaligned();
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ unable to copy parts of a pointer from memory at ALLOC+HALF_PTR
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/copy_half_a_pointer.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to previous error
-
diff --git a/src/tools/miri/tests/fail/erroneous_const.rs b/src/tools/miri/tests/fail/erroneous_const.rs
index d14998ccba2..d37837c7193 100644
--- a/src/tools/miri/tests/fail/erroneous_const.rs
+++ b/src/tools/miri/tests/fail/erroneous_const.rs
@@ -11,7 +11,7 @@ impl<T> PrintName<T> {
 
 fn no_codegen<T>() {
     if false {
-        let _ = PrintName::<T>::VOID; //~ERROR: post-monomorphization error
+        let _ = PrintName::<T>::VOID; //~NOTE: constant
     }
 }
 fn main() {
diff --git a/src/tools/miri/tests/fail/erroneous_const.stderr b/src/tools/miri/tests/fail/erroneous_const.stderr
index 8138d69f403..c32ebf67a11 100644
--- a/src/tools/miri/tests/fail/erroneous_const.stderr
+++ b/src/tools/miri/tests/fail/erroneous_const.stderr
@@ -6,21 +6,12 @@ LL |     const VOID: ! = panic!();
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: post-monomorphization error: referenced constant has errors
+note: erroneous constant used
   --> $DIR/erroneous_const.rs:LL:CC
    |
 LL |         let _ = PrintName::<T>::VOID;
-   |                 ^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
-   |
-   = note: inside `no_codegen::<i32>` at $DIR/erroneous_const.rs:LL:CC
-note: inside `main` at $DIR/erroneous_const.rs:LL:CC
-  --> $DIR/erroneous_const.rs:LL:CC
-   |
-LL |     no_codegen::<i32>();
-   |     ^^^^^^^^^^^^^^^^^^^
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+   |                 ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/src/tools/miri/tests/fail/memleak_rc.stderr b/src/tools/miri/tests/fail/memleak_rc.stderr
deleted file mode 100644
index 290de49c82c..00000000000
--- a/src/tools/miri/tests/fail/memleak_rc.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-The following memory was leaked: ALLOC (Rust heap, size: 32, align: 8) {
-    0x00 │ 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 │ ................
-    0x10 │ 00 00 00 00 00 00 00 00 ╾$HEX[a1765]<TAG>─╼ │ ........╾──────╼
-}
-
-error: the evaluated program leaked memory
-
-note: pass `-Zmiri-ignore-leaks` to disable this check
-
-error: aborting due to previous error
-
diff --git a/src/tools/miri/tests/fail/pointer_partial_overwrite.rs b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.rs
index 63f0649b8ed..d3a68fbdd01 100644
--- a/src/tools/miri/tests/fail/pointer_partial_overwrite.rs
+++ b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.rs
@@ -2,16 +2,13 @@
 //@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
 
 // Test what happens when we overwrite parts of a pointer.
-// Also see <https://github.com/rust-lang/miri/issues/2181>.
 
 fn main() {
     let mut p = &42;
     unsafe {
         let ptr: *mut _ = &mut p;
-        *(ptr as *mut u8) = 123; // if we ever support 8 bit pointers, this is gonna cause
-        // "attempted to interpret some raw bytes as a pointer address" instead of
-        // "attempted to read undefined bytes"
+        *(ptr as *mut u8) = 123; // this removes provenance from one of the bytes, meaning the entire ptr is considered to have no provenance.
     }
-    let x = *p; //~ ERROR: this operation requires initialized memory
+    let x = *p; //~ ERROR: no provenance
     panic!("this should never print: {}", x);
 }
diff --git a/src/tools/miri/tests/fail/pointer_partial_overwrite.stderr b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr
index 7d10b75e880..06e5ede8c77 100644
--- a/src/tools/miri/tests/fail/pointer_partial_overwrite.stderr
+++ b/src/tools/miri/tests/fail/provenance/pointer_partial_overwrite.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
   --> $DIR/pointer_partial_overwrite.rs:LL:CC
    |
 LL |     let x = *p;
-   |             ^^ using uninitialized data, but this operation requires initialized memory
+   |             ^^ dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs b/src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs
new file mode 100644
index 00000000000..170bc6e1ed1
--- /dev/null
+++ b/src/tools/miri/tests/fail/uninit_buffer_with_provenance.rs
@@ -0,0 +1,32 @@
+//@error-pattern: memory is uninitialized at [0x4..0x8]
+//@normalize-stderr-test: "a[0-9]+" -> "ALLOC"
+#![feature(strict_provenance)]
+
+// Test printing allocations that contain single-byte provenance.
+
+use std::alloc::{alloc, dealloc, Layout};
+use std::mem::{self, MaybeUninit};
+use std::slice::from_raw_parts;
+
+fn byte_with_provenance<T>(val: u8, prov: *const T) -> MaybeUninit<u8> {
+    let ptr = prov.with_addr(val as usize);
+    let bytes: [MaybeUninit<u8>; mem::size_of::<*const ()>()] = unsafe { mem::transmute(ptr) };
+    let lsb = if cfg!(target_endian = "little") { 0 } else { bytes.len() - 1 };
+    bytes[lsb]
+}
+
+fn main() {
+    let layout = Layout::from_size_align(16, 8).unwrap();
+    unsafe {
+        let ptr = alloc(layout);
+        let ptr_raw = ptr.cast::<MaybeUninit<u8>>();
+        *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8);
+        *ptr.add(1) = 0x12;
+        *ptr.add(2) = 0x13;
+        *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8);
+        let slice1 = from_raw_parts(ptr, 8);
+        let slice2 = from_raw_parts(ptr.add(8), 8);
+        drop(slice1.cmp(slice2));
+        dealloc(ptr, layout);
+    }
+}
diff --git a/src/tools/miri/tests/fail/uninit_buffer_with_provenance.stderr b/src/tools/miri/tests/fail/uninit_buffer_with_provenance.stderr
new file mode 100644
index 00000000000..715d76aa1c2
--- /dev/null
+++ b/src/tools/miri/tests/fail/uninit_buffer_with_provenance.stderr
@@ -0,0 +1,32 @@
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory
+  --> RUSTLIB/core/src/slice/cmp.rs:LL:CC
+   |
+LL |         let mut order = unsafe { memcmp(left.as_ptr(), right.as_ptr(), len) as isize };
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reading memory at ALLOC[0x0..0x8], but memory is uninitialized at [0x4..0x8], and this operation requires initialized memory
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `<u8 as core::slice::cmp::SliceOrd>::compare` at RUSTLIB/core/src/slice/cmp.rs:LL:CC
+   = note: inside `core::slice::cmp::<impl std::cmp::Ord for [u8]>::cmp` at RUSTLIB/core/src/slice/cmp.rs:LL:CC
+note: inside `main` at $DIR/uninit_buffer_with_provenance.rs:LL:CC
+  --> $DIR/uninit_buffer_with_provenance.rs:LL:CC
+   |
+LL |         drop(slice1.cmp(slice2));
+   |              ^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation:
+ALLOC (Rust heap, size: 16, align: 8) {
+    ╾42[ALLOC]<TAG> (1 ptr byte)╼ 12 13 ╾43[ALLOC]<TAG> (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░
+}
+ALLOC (global (static or const), size: 1, align: 1) {
+    2a                                              │ *
+}
+ALLOC (global (static or const), size: 1, align: 1) {
+    00                                              │ .
+}
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/pass/miri-alloc.rs b/src/tools/miri/tests/pass/miri-alloc.rs
new file mode 100644
index 00000000000..f6464b5bd01
--- /dev/null
+++ b/src/tools/miri/tests/pass/miri-alloc.rs
@@ -0,0 +1,29 @@
+#![feature(lang_items, start)]
+#![no_std]
+// windows tls dtors go through libstd right now, thus this test
+// cannot pass. When windows tls dtors go through the special magic
+// windows linker section, we can run this test on windows again.
+//@ignore-target-windows: no-std not supported on Windows
+
+extern "Rust" {
+    fn miri_alloc(size: usize, align: usize) -> *mut u8;
+    fn miri_dealloc(ptr: *mut u8, size: usize, align: usize);
+}
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+    unsafe {
+        let ptr = miri_alloc(123, 1);
+        core::ptr::write_bytes(ptr, 0u8, 123);
+        miri_dealloc(ptr, 123, 1);
+    }
+    0
+}
+
+#[panic_handler]
+fn panic_handler(_: &core::panic::PanicInfo) -> ! {
+    loop {}
+}
+
+#[lang = "eh_personality"]
+fn eh_personality() {}
diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs
new file mode 100644
index 00000000000..b18d903e36c
--- /dev/null
+++ b/src/tools/miri/tests/pass/provenance.rs
@@ -0,0 +1,139 @@
+#![feature(strict_provenance)]
+#![feature(pointer_byte_offsets)]
+use std::{mem, ptr};
+
+const PTR_SIZE: usize = mem::size_of::<&i32>();
+
+fn main() {
+    basic();
+    partial_overwrite_then_restore();
+    bytewise_ptr_methods();
+    bytewise_custom_memcpy();
+    bytewise_custom_memcpy_chunked();
+}
+
+/// Some basic smoke tests for provenance.
+fn basic() {
+    let x = &42;
+    let ptr = x as *const i32;
+    let addr: usize = unsafe { mem::transmute(ptr) }; // an integer without provenance
+    // But we can give provenance back via `with_addr`.
+    let ptr_back = ptr.with_addr(addr);
+    assert_eq!(unsafe { *ptr_back }, 42);
+
+    // It is preserved by MaybeUninit.
+    let addr_mu: mem::MaybeUninit<usize> = unsafe { mem::transmute(ptr) };
+    let ptr_back: *const i32 = unsafe { mem::transmute(addr_mu) };
+    assert_eq!(unsafe { *ptr_back }, 42);
+}
+
+/// Overwrite one byte of a pointer, then restore it.
+fn partial_overwrite_then_restore() {
+    unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit<u8>; PTR_SIZE] {
+        mem::transmute(ptr)
+    }
+
+    // Returns a value with the same provenance as `x` but 0 for the integer value.
+    // `x` must be initialized.
+    unsafe fn zero_with_provenance(x: mem::MaybeUninit<u8>) -> mem::MaybeUninit<u8> {
+        let ptr = [x; PTR_SIZE];
+        let ptr: *const i32 = mem::transmute(ptr);
+        let mut ptr = ptr.with_addr(0);
+        ptr_bytes(&mut ptr)[0]
+    }
+
+    unsafe {
+        let ptr = &42;
+        let mut ptr = ptr as *const i32;
+        // Get a bytewise view of the pointer.
+        let ptr_bytes = ptr_bytes(&mut ptr);
+
+        // The highest bytes must be 0 for this to work.
+        let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 };
+        assert_eq!(*ptr_bytes[hi].as_ptr().cast::<u8>(), 0);
+        // Overwrite provenance on the last byte.
+        ptr_bytes[hi] = mem::MaybeUninit::new(0);
+        // Restore it from the another byte.
+        ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]);
+
+        // Now ptr should be good again.
+        assert_eq!(*ptr, 42);
+    }
+}
+
+fn bytewise_ptr_methods() {
+    let mut ptr1 = &1;
+    let mut ptr2 = &2;
+
+    // Swap them, bytewise.
+    unsafe {
+        ptr::swap_nonoverlapping(
+            &mut ptr1 as *mut _ as *mut mem::MaybeUninit<u8>,
+            &mut ptr2 as *mut _ as *mut mem::MaybeUninit<u8>,
+            mem::size_of::<&i32>(),
+        );
+    }
+
+    // Make sure they still work.
+    assert_eq!(*ptr1, 2);
+    assert_eq!(*ptr2, 1);
+
+    // TODO: also test ptr::swap, ptr::copy, ptr::copy_nonoverlapping.
+}
+
+fn bytewise_custom_memcpy() {
+    unsafe fn memcpy<T>(to: *mut T, from: *const T) {
+        let to = to.cast::<mem::MaybeUninit<u8>>();
+        let from = from.cast::<mem::MaybeUninit<u8>>();
+        for i in 0..mem::size_of::<T>() {
+            let b = from.add(i).read();
+            to.add(i).write(b);
+        }
+    }
+
+    let ptr1 = &1;
+    let mut ptr2 = &2;
+
+    // Copy, bytewise.
+    unsafe { memcpy(&mut ptr2, &ptr1) };
+
+    // Make sure they still work.
+    assert_eq!(*ptr1, 1);
+    assert_eq!(*ptr2, 1);
+}
+
+fn bytewise_custom_memcpy_chunked() {
+    unsafe fn memcpy<T>(to: *mut T, from: *const T) {
+        assert!(mem::size_of::<T>() % mem::size_of::<usize>() == 0);
+        let count = mem::size_of::<T>() / mem::size_of::<usize>();
+        let to = to.cast::<mem::MaybeUninit<usize>>();
+        let from = from.cast::<mem::MaybeUninit<usize>>();
+        for i in 0..count {
+            let b = from.add(i).read();
+            to.add(i).write(b);
+        }
+    }
+
+    // Prepare an array where pointers are stored at... interesting... offsets.
+    let mut data = [0usize; 2 * PTR_SIZE];
+    let mut offsets = vec![];
+    for i in 0..mem::size_of::<usize>() {
+        // We have 2*PTR_SIZE room for each of these pointers.
+        let base = i * 2 * PTR_SIZE;
+        // This one is mis-aligned by `i`.
+        let offset = base + i;
+        offsets.push(offset);
+        // Store it there.
+        unsafe { data.as_mut_ptr().byte_add(offset).cast::<&i32>().write_unaligned(&42) };
+    }
+
+    // Now memcpy that.
+    let mut data2 = [0usize; 2 * PTR_SIZE];
+    unsafe { memcpy(&mut data2, &data) };
+
+    // And check the result.
+    for &offset in &offsets {
+        let ptr = unsafe { data2.as_ptr().byte_add(offset).cast::<&i32>().read_unaligned() };
+        assert_eq!(*ptr, 42);
+    }
+}
diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml
index 80bb3b5d6a8..07cf89f7d33 100644
--- a/src/tools/rustc-workspace-hack/Cargo.toml
+++ b/src/tools/rustc-workspace-hack/Cargo.toml
@@ -82,6 +82,8 @@ regex = { version = "1.5.6" }
 serde_json = { version = "1.0.31", features = ["raw_value", "unbounded_depth"] }
 syn = { version = "1", features = ['full', 'visit'] }
 url = { version = "2.0", features = ['serde'] }
+# Ensure default features of rand, which are disabled in some scenarios.
+rand = { version = "0.8.5" }
 
 [target.'cfg(not(windows))'.dependencies]
 openssl = { version = "0.10.35", optional = true }
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index f5c1ee5fdd1..ccc2fd0d5f5 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -260,7 +260,9 @@ impl Rewrite for ast::NestedMetaItem {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
         match self {
             ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
-            ast::NestedMetaItem::Literal(ref l) => rewrite_literal(context, l, shape),
+            ast::NestedMetaItem::Literal(ref l) => {
+                rewrite_literal(context, l.token_lit, l.span, shape)
+            }
         }
     }
 }
@@ -318,7 +320,7 @@ impl Rewrite for ast::MetaItem {
                 // we might be better off ignoring the fact that the attribute
                 // is longer than the max width and continue on formatting.
                 // See #2479 for example.
-                let value = rewrite_literal(context, literal, lit_shape)
+                let value = rewrite_literal(context, literal.token_lit, literal.span, lit_shape)
                     .unwrap_or_else(|| context.snippet(literal.span).to_owned());
                 format!("{} = {}", path, value)
             }
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 7750df0fff3..b4f1a178dbf 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -3,7 +3,7 @@ use std::cmp::min;
 
 use itertools::Itertools;
 use rustc_ast::token::{Delimiter, LitKind};
-use rustc_ast::{ast, ptr};
+use rustc_ast::{ast, ptr, token};
 use rustc_span::{BytePos, Span};
 
 use crate::chains::rewrite_chain;
@@ -75,12 +75,12 @@ pub(crate) fn format_expr(
             choose_separator_tactic(context, expr.span),
             None,
         ),
-        ast::ExprKind::Lit(ref l) => {
-            if let Some(expr_rw) = rewrite_literal(context, l, shape) {
+        ast::ExprKind::Lit(token_lit) => {
+            if let Some(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) {
                 Some(expr_rw)
             } else {
-                if let LitKind::StrRaw(_) = l.token_lit.kind {
-                    Some(context.snippet(l.span).trim().into())
+                if let LitKind::StrRaw(_) = token_lit.kind {
+                    Some(context.snippet(expr.span).trim().into())
                 } else {
                     None
                 }
@@ -274,9 +274,9 @@ pub(crate) fn format_expr(
 
             fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool {
                 match lhs.kind {
-                    ast::ExprKind::Lit(ref lit) => match lit.kind {
-                        ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
-                            context.snippet(lit.span).ends_with('.')
+                    ast::ExprKind::Lit(token_lit) => match token_lit.kind {
+                        token::LitKind::Float if token_lit.suffix.is_none() => {
+                            context.snippet(lhs.span).ends_with('.')
                         }
                         _ => false,
                     },
@@ -1185,14 +1185,15 @@ pub(crate) fn is_unsafe_block(block: &ast::Block) -> bool {
 
 pub(crate) fn rewrite_literal(
     context: &RewriteContext<'_>,
-    l: &ast::Lit,
+    token_lit: token::Lit,
+    span: Span,
     shape: Shape,
 ) -> Option<String> {
-    match l.kind {
-        ast::LitKind::Str(_, ast::StrStyle::Cooked) => rewrite_string_lit(context, l.span, shape),
-        ast::LitKind::Int(..) => rewrite_int_lit(context, l, shape),
+    match token_lit.kind {
+        token::LitKind::Str => rewrite_string_lit(context, span, shape),
+        token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape),
         _ => wrap_str(
-            context.snippet(l.span).to_owned(),
+            context.snippet(span).to_owned(),
             context.config.max_width(),
             shape,
         ),
@@ -1225,9 +1226,13 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) ->
     )
 }
 
-fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) -> Option<String> {
-    let span = lit.span;
-    let symbol = lit.token_lit.symbol.as_str();
+fn rewrite_int_lit(
+    context: &RewriteContext<'_>,
+    token_lit: token::Lit,
+    span: Span,
+    shape: Shape,
+) -> Option<String> {
+    let symbol = token_lit.symbol.as_str();
 
     if let Some(symbol_stripped) = symbol.strip_prefix("0x") {
         let hex_lit = match context.config.hex_literal_case() {
@@ -1240,9 +1245,7 @@ fn rewrite_int_lit(context: &RewriteContext<'_>, lit: &ast::Lit, shape: Shape) -
                 format!(
                     "0x{}{}",
                     hex_lit,
-                    lit.token_lit
-                        .suffix
-                        .map_or(String::new(), |s| s.to_string())
+                    token_lit.suffix.map_or(String::new(), |s| s.to_string())
                 ),
                 context.config.max_width(),
                 shape,
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 35fa968f977..f1f5a194157 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -117,6 +117,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "env_logger",
     "expect-test",
     "fallible-iterator", // dependency of `thorin`
+    "fastrand",
     "filetime",
     "fixedbitset",
     "flate2",
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 541380cebde..e3a094caf91 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -60,7 +60,7 @@ const ANNOTATIONS_TO_IGNORE: &[&str] = &[
 // Intentionally written in decimal rather than hex
 const PROBLEMATIC_CONSTS: &[u32] = &[
     184594741, 2880289470, 2881141438, 2965027518, 2976579765, 3203381950, 3405691582, 3405697037,
-    3735927486, 4027431614, 4276992702,
+    3735927486, 3735932941, 4027431614, 4276992702,
 ];
 
 /// Parser states for `line_is_url`.
diff --git a/src/tools/x/src/main.rs b/src/tools/x/src/main.rs
index 9187c3551d7..02c364dabf9 100644
--- a/src/tools/x/src/main.rs
+++ b/src/tools/x/src/main.rs
@@ -8,7 +8,8 @@
 //! `x.py`, in that order of preference.
 
 use std::{
-    env, io,
+    env::{self, consts::EXE_EXTENSION},
+    io,
     process::{self, Command, ExitStatus},
 };
 
@@ -27,12 +28,12 @@ fn python() -> &'static str {
 
     for dir in env::split_paths(&val) {
         // `python` should always take precedence over python2 / python3 if it exists
-        if dir.join(PYTHON).exists() {
+        if dir.join(PYTHON).with_extension(EXE_EXTENSION).exists() {
             return PYTHON;
         }
 
-        python2 |= dir.join(PYTHON2).exists();
-        python3 |= dir.join(PYTHON3).exists();
+        python2 |= dir.join(PYTHON2).with_extension(EXE_EXTENSION).exists();
+        python3 |= dir.join(PYTHON3).with_extension(EXE_EXTENSION).exists();
     }
 
     // try 3 before 2
diff --git a/triagebot.toml b/triagebot.toml
index 8efd9af41a4..985e065652d 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -182,6 +182,7 @@ trigger_files = [
     "x.ps1",
     "src/bootstrap",
     "src/tools/rust-installer",
+    "src/tools/x",
     "configure",
     "Cargo.toml",
     "Cargo.lock",
@@ -583,3 +584,4 @@ fallback = [
 "/src/tools/rustdoc-js" =                    ["rustdoc"]
 "/src/tools/rustdoc-themes" =                ["rustdoc"]
 "/src/tools/tidy" =                          ["bootstrap"]
+"/src/tools/x" =                             ["bootstrap"]