about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules2
-rw-r--r--Cargo.lock37
-rw-r--r--RELEASES.md20
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs12
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs15
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/constant.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs15
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs32
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs8
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs13
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs46
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs9
-rw-r--r--compiler/rustc_data_structures/src/lib.rs19
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs80
-rw-r--r--compiler/rustc_hir/src/definitions.rs3
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl12
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs71
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs55
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs11
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs6
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs583
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs180
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs1517
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs1177
-rw-r--r--compiler/rustc_middle/src/mir/query.rs3
-rw-r--r--compiler/rustc_middle/src/mir/statement.rs441
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs24
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs368
-rw-r--r--compiler/rustc_middle/src/query/erase.rs16
-rw-r--r--compiler/rustc_middle/src/query/keys.rs5
-rw-r--r--compiler/rustc_middle/src/query/mod.rs8
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs66
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs2
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs48
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs15
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs1
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs29
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs10
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs1
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs6
-rw-r--r--compiler/rustc_mir_transform/src/check_alignment.rs2
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs101
-rw-r--r--compiler/rustc_mir_transform/src/coverage/debug.rs797
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs7
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs125
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs49
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs2
-rw-r--r--compiler/rustc_mir_transform/src/large_enums.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_zsts.rs1
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs13
-rw-r--r--compiler/rustc_query_system/src/dep_graph/serialized.rs8
-rw-r--r--compiler/rustc_resolve/src/imports.rs6
-rw-r--r--compiler/rustc_serialize/src/leb128.rs20
-rw-r--r--compiler/rustc_serialize/src/lib.rs3
-rw-r--r--compiler/rustc_serialize/src/opaque.rs276
-rw-r--r--compiler/rustc_serialize/tests/leb128.rs14
-rw-r--r--compiler/rustc_session/src/options.rs7
-rw-r--r--compiler/rustc_smir/src/rustc_smir/alloc.rs19
-rw-r--r--compiler/rustc_span/src/source_map.rs33
-rw-r--r--compiler/rustc_span/src/source_map/tests.rs27
-rw-r--r--compiler/rustc_target/src/abi/call/loongarch.rs11
-rw-r--r--compiler/rustc_target/src/abi/call/riscv.rs11
-rw-r--r--compiler/rustc_target/src/abi/mod.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs9
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs97
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs235
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/build.rs (renamed from compiler/rustc_trait_selection/src/solve/inspect.rs)238
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs220
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs73
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs12
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs26
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs25
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs13
-rw-r--r--library/core/src/ffi/c_str.rs106
-rw-r--r--library/core/src/num/f32.rs1
-rw-r--r--library/core/src/num/f64.rs1
-rw-r--r--library/core/src/panic.rs4
-rw-r--r--library/panic_abort/src/android.rs4
-rw-r--r--library/panic_abort/src/lib.rs7
-rw-r--r--library/panic_unwind/src/lib.rs4
-rw-r--r--library/std/Cargo.toml6
-rw-r--r--library/std/build.rs1
-rw-r--r--library/std/src/fs.rs2
-rw-r--r--library/std/src/io/error.rs1
-rw-r--r--library/std/src/lib.rs4
-rw-r--r--library/std/src/net/tcp.rs2
-rw-r--r--library/std/src/net/udp.rs2
-rw-r--r--library/std/src/os/mod.rs2
-rw-r--r--library/std/src/os/xous/ffi.rs647
-rw-r--r--library/std/src/os/xous/ffi/definitions.rs283
-rw-r--r--library/std/src/os/xous/ffi/definitions/memoryflags.rs176
-rw-r--r--library/std/src/os/xous/mod.rs17
-rw-r--r--library/std/src/os/xous/services.rs132
-rw-r--r--library/std/src/os/xous/services/log.rs63
-rw-r--r--library/std/src/os/xous/services/systime.rs28
-rw-r--r--library/std/src/os/xous/services/ticktimer.rs42
-rw-r--r--library/std/src/panicking.rs78
-rw-r--r--library/std/src/process.rs2
-rw-r--r--library/std/src/sys/mod.rs3
-rw-r--r--library/std/src/sys/unix/thread.rs6
-rw-r--r--library/std/src/sys/xous/alloc.rs62
-rw-r--r--library/std/src/sys/xous/locks/condvar.rs111
-rw-r--r--library/std/src/sys/xous/locks/mod.rs7
-rw-r--r--library/std/src/sys/xous/locks/mutex.rs116
-rw-r--r--library/std/src/sys/xous/locks/rwlock.rs72
-rw-r--r--library/std/src/sys/xous/mod.rs37
-rw-r--r--library/std/src/sys/xous/os.rs147
-rw-r--r--library/std/src/sys/xous/stdio.rs131
-rw-r--r--library/std/src/sys/xous/thread.rs144
-rw-r--r--library/std/src/sys/xous/thread_local_key.rs190
-rw-r--r--library/std/src/sys/xous/time.rs57
-rw-r--r--library/std/src/sys_common/mod.rs1
-rw-r--r--library/std/src/sys_common/thread_info.rs37
-rw-r--r--src/bootstrap/builder.rs5
-rw-r--r--src/bootstrap/job.rs2
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh21
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version2
-rw-r--r--src/doc/rustdoc/src/SUMMARY.md1
-rw-r--r--src/doc/rustdoc/src/how-to-read-rustdoc.md55
-rw-r--r--src/doc/rustdoc/src/read-documentation/search.md237
-rw-r--r--src/doc/style-guide/src/README.md13
-rw-r--r--src/doc/style-guide/src/expressions.md28
-rw-r--r--src/librustdoc/clean/types.rs4
-rw-r--r--src/librustdoc/clean/utils.rs11
-rw-r--r--src/librustdoc/html/markdown.rs275
-rw-r--r--src/librustdoc/html/markdown/tests.rs21
-rw-r--r--src/librustdoc/html/render/mod.rs19
-rw-r--r--src/librustdoc/html/render/search_index.rs234
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css12
-rw-r--r--src/librustdoc/html/static/js/externs.js54
-rw-r--r--src/librustdoc/html/static/js/search.js747
-rw-r--r--src/librustdoc/passes/check_custom_code_classes.rs6
m---------src/llvm-project0
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs2
-rw-r--r--src/tools/compiletest/src/common.rs20
-rw-r--r--src/tools/compiletest/src/header/needs.rs28
-rw-r--r--src/tools/compiletest/src/header/tests.rs234
-rw-r--r--src/tools/compiletest/src/runtest.rs19
-rw-r--r--src/tools/compiletest/src/util.rs102
-rw-r--r--src/tools/miri/Cargo.lock88
-rw-r--r--src/tools/miri/Cargo.toml2
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/helpers.rs37
-rw-r--r--src/tools/miri/src/shims/backtrace.rs5
-rw-r--r--src/tools/miri/src/shims/intrinsics/simd.rs19
-rw-r--r--src/tools/miri/src/shims/x86/mod.rs158
-rw-r--r--src/tools/miri/src/shims/x86/sse.rs248
-rw-r--r--src/tools/miri/src/shims/x86/sse2.rs380
-rw-r--r--src/tools/rustdoc-js/tester.js4
-rw-r--r--src/tools/rustdoc-themes/main.rs3
-rw-r--r--src/tools/tidy/src/deps.rs2
-rw-r--r--tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs5
-rw-r--r--tests/codegen/sanitizer/kcfi-add-kcfi-flag.rs13
-rw-r--r--tests/codegen/sanitizer/memory-track-origins.rs11
-rw-r--r--tests/codegen/sanitizer/no-sanitize-inlining.rs5
-rw-r--r--tests/codegen/sanitizer/no-sanitize.rs2
-rw-r--r--tests/codegen/sanitizer/sanitizer-recover.rs1
-rw-r--r--tests/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot6
-rw-r--r--tests/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot13
-rw-r--r--tests/mir-opt/coverage_graphviz.rs20
-rw-r--r--tests/mir-opt/dont_inline_type_id.call.Inline.diff20
-rw-r--r--tests/mir-opt/dont_inline_type_id.rs15
-rw-r--r--tests/mir-opt/inline_generically_if_sized.call.Inline.diff24
-rw-r--r--tests/mir-opt/inline_generically_if_sized.rs17
-rw-r--r--tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir2
-rw-r--r--tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir2
-rw-r--r--tests/run-make/dump-ice-to-disk/check.sh4
-rw-r--r--tests/rustdoc-gui/search-corrections.goml50
-rw-r--r--tests/rustdoc-gui/search-tab.goml86
-rw-r--r--tests/rustdoc-js-std/option-type-signatures.js62
-rw-r--r--tests/rustdoc-js-std/vec-type-signatures.js22
-rw-r--r--tests/rustdoc-js/generics-match-ambiguity.js1
-rw-r--r--tests/rustdoc-js/generics-trait.js8
-rw-r--r--tests/rustdoc-js/generics-unbox.js38
-rw-r--r--tests/rustdoc-js/generics-unbox.rs36
-rw-r--r--tests/rustdoc-js/type-parameters.js87
-rw-r--r--tests/rustdoc-js/type-parameters.rs15
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs8
-rw-r--r--tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr26
-rw-r--r--tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs5
-rw-r--r--tests/ui/abi/compatibility.rs3
-rw-r--r--tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs38
-rw-r--r--tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr5
-rw-r--r--tests/ui/async-await/issues/issue-66695-static-refs.rs4
-rw-r--r--tests/ui/async-await/issues/issue-67611-static-mut-refs.rs4
-rw-r--r--tests/ui/async-await/missed-capture-issue-107414.rs9
-rw-r--r--tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr8
-rw-r--r--tests/ui/binding/match-beginning-vert.rs4
-rw-r--r--tests/ui/coherence/coherence-overlap-downstream-inherent.next.stderr (renamed from tests/ui/coherence/coherence-overlap-downstream-inherent.stderr)4
-rw-r--r--tests/ui/coherence/coherence-overlap-downstream-inherent.old.stderr23
-rw-r--r--tests/ui/coherence/coherence-overlap-downstream-inherent.rs3
-rw-r--r--tests/ui/coherence/coherence-overlap-downstream.next.stderr (renamed from tests/ui/coherence/coherence-overlap-downstream.stderr)4
-rw-r--r--tests/ui/coherence/coherence-overlap-downstream.old.stderr21
-rw-r--r--tests/ui/coherence/coherence-overlap-downstream.rs3
-rw-r--r--tests/ui/coherence/coherence-overlap-issue-23516-inherent.next.stderr (renamed from tests/ui/coherence/coherence-overlap-issue-23516-inherent.stderr)2
-rw-r--r--tests/ui/coherence/coherence-overlap-issue-23516-inherent.old.stderr14
-rw-r--r--tests/ui/coherence/coherence-overlap-issue-23516-inherent.rs3
-rw-r--r--tests/ui/coherence/coherence-overlap-issue-23516.next.stderr (renamed from tests/ui/coherence/coherence-overlap-issue-23516.stderr)2
-rw-r--r--tests/ui/coherence/coherence-overlap-issue-23516.old.stderr13
-rw-r--r--tests/ui/coherence/coherence-overlap-issue-23516.rs3
-rw-r--r--tests/ui/coherence/inter-crate-ambiguity-causes-notes.next.stderr (renamed from tests/ui/coherence/inter-crate-ambiguity-causes-notes.stderr)2
-rw-r--r--tests/ui/coherence/inter-crate-ambiguity-causes-notes.old.stderr14
-rw-r--r--tests/ui/coherence/inter-crate-ambiguity-causes-notes.rs3
-rw-r--r--tests/ui/const-generics/late-bound-vars/in_closure.rs20
-rw-r--r--tests/ui/const-generics/late-bound-vars/in_closure.stderr30
-rw-r--r--tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.rs13
-rw-r--r--tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr8
-rw-r--r--tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.rs15
-rw-r--r--tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.stderr10
-rw-r--r--tests/ui/const-generics/late-bound-vars/simple.rs27
-rw-r--r--tests/ui/const-generics/late-bound-vars/simple.stderr29
-rw-r--r--tests/ui/const_prop/dont-propagate-generic-instance-2.rs26
-rw-r--r--tests/ui/const_prop/dont-propagate-generic-instance.rs24
-rw-r--r--tests/ui/consts/escaping-bound-var.rs13
-rw-r--r--tests/ui/consts/escaping-bound-var.stderr20
-rw-r--r--tests/ui/drop/dynamic-drop.rs18
-rw-r--r--tests/ui/generic-associated-types/bugs/issue-88460.stderr8
-rw-r--r--tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr8
-rw-r--r--tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr8
-rw-r--r--tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr5
-rw-r--r--tests/ui/impl-trait/coherence-treats-tait-ambig.rs3
-rw-r--r--tests/ui/impl-trait/coherence-treats-tait-ambig.stderr (renamed from tests/ui/impl-trait/coherence-treats-tait-ambig.next.stderr)2
-rw-r--r--tests/ui/match/issue-115681.rs32
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/const-expr.rs26
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs97
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.stderr67
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let.rs41
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/type-inference.rs16
-rw-r--r--tests/ui/sanitize/cfg.rs22
-rw-r--r--tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr2
-rw-r--r--tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr6
-rw-r--r--tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.rs2
-rw-r--r--tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.stderr2
-rw-r--r--tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs2
-rw-r--r--tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr2
-rw-r--r--tests/ui/type-alias-impl-trait/escaping-bound-var.rs22
-rw-r--r--tests/ui/type-alias-impl-trait/escaping-bound-var.stderr8
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs29
-rw-r--r--tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr5
-rw-r--r--tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs40
-rw-r--r--tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr5
-rw-r--r--tests/ui/type-alias-impl-trait/variance.rs30
-rw-r--r--tests/ui/type-alias-impl-trait/variance.stderr62
-rw-r--r--tests/ui/unreachable-code.rs15
-rw-r--r--triagebot.toml2
262 files changed, 9383 insertions, 5618 deletions
diff --git a/.gitmodules b/.gitmodules
index a13a2f5e01b..f5025097a18 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -33,7 +33,7 @@
 [submodule "src/llvm-project"]
 	path = src/llvm-project
 	url = https://github.com/rust-lang/llvm-project.git
-	branch = rustc/17.0-2023-07-29
+	branch = rustc/17.0-2023-09-19
 	shallow = true
 [submodule "src/doc/embedded-book"]
 	path = src/doc/embedded-book
diff --git a/Cargo.lock b/Cargo.lock
index a93078829e2..d9aaedb8544 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -627,13 +627,13 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
 [[package]]
 name = "colored"
-version = "2.0.0"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
+checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
 dependencies = [
- "atty",
+ "is-terminal",
  "lazy_static",
- "winapi",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -644,9 +644,9 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335"
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.100"
+version = "0.1.101"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c0f24437059853f0fa64afc51f338f93647a3de4cf3358ba1bb4171a199775"
+checksum = "01a6d58e9c3408138099a396a98fd0d0e6cfb25d723594d2ae48b5004513fd5b"
 dependencies = [
  "cc",
  "rustc-std-workspace-core",
@@ -1171,19 +1171,6 @@ dependencies = [
 
 [[package]]
 name = "env_logger"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
-dependencies = [
- "atty",
- "humantime 2.1.0",
- "log",
- "regex",
- "termcolor",
-]
-
-[[package]]
-name = "env_logger"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
@@ -2142,9 +2129,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
 
 [[package]]
 name = "libc"
-version = "0.2.147"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 dependencies = [
  "rustc-std-workspace-core",
 ]
@@ -2454,7 +2441,7 @@ version = "0.1.0"
 dependencies = [
  "colored",
  "ctrlc",
- "env_logger 0.9.3",
+ "env_logger 0.10.0",
  "getrandom",
  "lazy_static",
  "libc",
@@ -5591,11 +5578,11 @@ dependencies = [
 
 [[package]]
 name = "tracing-tree"
-version = "0.2.3"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f9742d8df709837409dbb22aa25dd7769c260406f20ff48a2320b80a4a6aed0"
+checksum = "92d6b63348fad3ae0439b8bebf8d38fb5bda0b115d7a8a7e6f165f12790c58c3"
 dependencies = [
- "atty",
+ "is-terminal",
  "nu-ansi-term",
  "tracing-core",
  "tracing-log",
diff --git a/RELEASES.md b/RELEASES.md
index c5ac070b317..d390f2b7f5e 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,13 @@
+Version 1.72.1 (2023-09-19)
+===========================
+
+- [Adjust codegen change to improve LLVM codegen](https://github.com/rust-lang/rust/pull/115236)
+- [rustdoc: Fix self ty params in objects with lifetimes](https://github.com/rust-lang/rust/pull/115276)
+- [Fix regression in compile times](https://github.com/rust-lang/rust/pull/114948)
+- Resolve some ICE regressions in the compiler:
+  - [#115215](https://github.com/rust-lang/rust/pull/115215)
+  - [#115559](https://github.com/rust-lang/rust/pull/115559)
+
 Version 1.72.0 (2023-08-24)
 ==========================
 
@@ -103,6 +113,16 @@ Compatibility Notes
   to a registry.
   [#12291](https://github.com/rust-lang/cargo/pull/12291)
 
+Version 1.71.1 (2023-08-03)
+===========================
+
+- [Fix CVE-2023-38497: Cargo did not respect the umask when extracting dependencies](https://github.com/rust-lang/cargo/security/advisories/GHSA-j3xp-wfr4-hx87)
+- [Fix bash completion for users of Rustup](https://github.com/rust-lang/rust/pull/113579)
+- [Do not show `suspicious_double_ref_op` lint when calling `borrow()`](https://github.com/rust-lang/rust/pull/112517)
+- [Fix ICE: substitute types before checking inlining compatibility](https://github.com/rust-lang/rust/pull/113802)
+- [Fix ICE: don't use `can_eq` in `derive(..)` suggestion for missing method](https://github.com/rust-lang/rust/pull/111516)
+- [Fix building Rust 1.71.0 from the source tarball](https://github.com/rust-lang/rust/issues/113678)
+
 Version 1.71.0 (2023-07-13)
 ==========================
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 32046b0febf..5b172b863ab 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1431,12 +1431,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         ),
                     ImplTraitContext::Universal => {
                         let span = t.span;
-                        self.create_def(
-                            self.current_hir_id_owner.def_id,
-                            *def_node_id,
-                            DefPathData::ImplTrait,
-                            span,
-                        );
 
                         // HACK: pprust breaks strings with newlines when the type
                         // gets too long. We don't want these to show up in compiler
@@ -1447,6 +1441,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             span,
                         );
 
+                        self.create_def(
+                            self.current_hir_id_owner.def_id,
+                            *def_node_id,
+                            DefPathData::TypeNs(ident.name),
+                            span,
+                        );
                         let (param, bounds, path) = self.lower_universal_param_and_bounds(
                             *def_node_id,
                             span,
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 0d9bd3cf240..151674b2d6d 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -2,7 +2,8 @@
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::mir::interpret::{read_target_uint, AllocId, ConstValue, GlobalAlloc, Scalar};
+use rustc_middle::mir::interpret::{read_target_uint, AllocId, GlobalAlloc, Scalar};
+use rustc_middle::mir::ConstValue;
 
 use cranelift_module::*;
 
@@ -183,15 +184,11 @@ pub(crate) fn codegen_const_value<'tcx>(
                 .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
             layout,
         ),
-        ConstValue::Slice { data, start, end } => {
+        ConstValue::Slice { data, meta } => {
             let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
-            let ptr = pointer_for_allocation(fx, alloc_id)
-                .offset_i64(fx, i64::try_from(start).unwrap())
-                .get_addr(fx);
-            let len = fx
-                .bcx
-                .ins()
-                .iconst(fx.pointer_type, i64::try_from(end.checked_sub(start).unwrap()).unwrap());
+            let ptr = pointer_for_allocation(fx, alloc_id).get_addr(fx);
+            // FIXME: the `try_from` here can actually fail, e.g. for very long ZST slices.
+            let len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(meta).unwrap());
             CValue::by_val_pair(ptr, len, layout)
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index 5a68075991f..641ac3eb808 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -1,7 +1,7 @@
 #![allow(non_camel_case_types)]
 
 use rustc_hir::LangItem;
-use rustc_middle::mir::interpret::ConstValue;
+use rustc_middle::mir;
 use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
 use rustc_span::Span;
 
@@ -194,10 +194,10 @@ pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 pub fn asm_const_to_str<'tcx>(
     tcx: TyCtxt<'tcx>,
     sp: Span,
-    const_value: ConstValue<'tcx>,
+    const_value: mir::ConstValue<'tcx>,
     ty_and_layout: TyAndLayout<'tcx>,
 ) -> String {
-    let ConstValue::Scalar(scalar) = const_value else {
+    let mir::ConstValue::Scalar(scalar) = const_value else {
         span_bug!(sp, "expected Scalar for promoted asm const, but got {:#?}", const_value)
     };
     let value = scalar.assert_bits(ty_and_layout.size);
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index 263b41ed880..7fec4047a95 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -2,7 +2,7 @@ use crate::errors;
 use crate::mir::operand::OperandRef;
 use crate::traits::*;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
+use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::layout::HasTyCtxt;
 use rustc_middle::ty::{self, Ty};
 use rustc_target::abi::Abi;
@@ -20,7 +20,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         OperandRef::from_const(bx, val, ty)
     }
 
-    pub fn eval_mir_constant(&self, constant: &mir::Constant<'tcx>) -> ConstValue<'tcx> {
+    pub fn eval_mir_constant(&self, constant: &mir::Constant<'tcx>) -> mir::ConstValue<'tcx> {
         self.monomorphize(constant.literal)
             .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span))
             .expect("erroneous constant not captured by required_consts")
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index e192d16ff38..9205acc527e 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -6,8 +6,8 @@ use crate::glue;
 use crate::traits::*;
 use crate::MemFlags;
 
-use rustc_middle::mir;
-use rustc_middle::mir::interpret::{alloc_range, ConstValue, Pointer, Scalar};
+use rustc_middle::mir::interpret::{alloc_range, Pointer, Scalar};
+use rustc_middle::mir::{self, ConstValue};
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::Ty;
 use rustc_target::abi::{self, Abi, Align, Size};
@@ -86,7 +86,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
 
     pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
         bx: &mut Bx,
-        val: ConstValue<'tcx>,
+        val: mir::ConstValue<'tcx>,
         ty: Ty<'tcx>,
     ) -> Self {
         let layout = bx.layout_of(ty);
@@ -100,15 +100,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
                 OperandValue::Immediate(llval)
             }
             ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
-            ConstValue::Slice { data, start, end } => {
+            ConstValue::Slice { data, meta } => {
                 let Abi::ScalarPair(a_scalar, _) = layout.abi else {
                     bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
                 };
                 let a = Scalar::from_pointer(
-                    Pointer::new(
-                        bx.tcx().reserve_and_set_memory_alloc(data),
-                        Size::from_bytes(start),
-                    ),
+                    Pointer::new(bx.tcx().reserve_and_set_memory_alloc(data), Size::ZERO),
                     &bx.tcx(),
                 );
                 let a_llval = bx.scalar_to_backend(
@@ -116,7 +113,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
                     a_scalar,
                     bx.scalar_pair_element_backend_type(layout, 0, true),
                 );
-                let b_llval = bx.const_usize((end - start) as u64);
+                let b_llval = bx.const_usize(meta);
                 OperandValue::Pair(a_llval, b_llval)
             }
             ConstValue::Indirect { alloc_id, offset } => {
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 807794a2a59..3d758cd01d3 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -4,9 +4,9 @@ use crate::errors::ConstEvalError;
 use either::{Left, Right};
 
 use rustc_hir::def::DefKind;
-use rustc_middle::mir;
 use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo};
 use rustc_middle::mir::pretty::write_allocation_bytes;
+use rustc_middle::mir::{self, ConstAlloc, ConstValue};
 use rustc_middle::traits::Reveal;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -18,9 +18,8 @@ use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
 use crate::errors;
 use crate::interpret::eval_nullary_intrinsic;
 use crate::interpret::{
-    intern_const_alloc_recursive, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, Immediate,
-    InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
-    StackPopCleanup,
+    intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx,
+    InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup,
 };
 
 // Returns a pointer to where the result lives
@@ -152,19 +151,26 @@ pub(super) fn op_to_const<'tcx>(
             Immediate::Scalar(x) => ConstValue::Scalar(x),
             Immediate::ScalarPair(a, b) => {
                 debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
-                // FIXME: assert that this has an appropriate type.
-                // Currently we actually get here for non-[u8] slices during valtree construction!
-                let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to actually allocated memory";
+                // This codepath solely exists for `valtree_to_const_value` to not need to generate
+                // a `ConstValue::Indirect` for wide references, so it is tightly restricted to just
+                // that case.
+                let pointee_ty = imm.layout.ty.builtin_deref(false).unwrap().ty; // `false` = no raw ptrs
+                debug_assert!(
+                    matches!(
+                        ecx.tcx.struct_tail_without_normalization(pointee_ty).kind(),
+                        ty::Str | ty::Slice(..),
+                    ),
+                    "`ConstValue::Slice` is for slice-tailed types only, but got {}",
+                    imm.layout.ty,
+                );
+                let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to the beginning of an actual allocation";
                 // We know `offset` is relative to the allocation, so we can use `into_parts`.
-                // We use `ConstValue::Slice` so that we don't have to generate an allocation for
-                // `ConstValue::Indirect` here.
                 let (alloc_id, offset) = a.to_pointer(ecx).expect(msg).into_parts();
                 let alloc_id = alloc_id.expect(msg);
                 let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
-                let start = offset.bytes_usize();
-                let len = b.to_target_usize(ecx).expect(msg);
-                let len: usize = len.try_into().unwrap();
-                ConstValue::Slice { data, start, end: start + len }
+                assert!(offset == abi::Size::ZERO, "{}", msg);
+                let meta = b.to_target_usize(ecx).expect(msg);
+                ConstValue::Slice { data, meta }
             }
             Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
         },
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 5327fa5ce39..886d7972a15 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,7 +1,7 @@
 // Not in interpret to make sure we do not use private implementation details
 
 use crate::errors::MaxNumNodesInConstErr;
-use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, Scalar};
+use crate::interpret::{intern_const_alloc_recursive, InternKind, InterpCx, Scalar};
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -22,7 +22,7 @@ pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value};
 pub(crate) fn const_caller_location(
     tcx: TyCtxt<'_>,
     (file, line, col): (Symbol, u32, u32),
-) -> ConstValue<'_> {
+) -> mir::ConstValue<'_> {
     trace!("const_caller_location: {}:{}:{}", file, line, col);
     let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
 
@@ -30,7 +30,7 @@ pub(crate) fn const_caller_location(
     if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
         bug!("intern_const_alloc_recursive should not error in this case")
     }
-    ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
+    mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
 }
 
 // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
@@ -87,7 +87,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
 #[instrument(skip(tcx), level = "debug")]
 pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
     tcx: TyCtxt<'tcx>,
-    val: ConstValue<'tcx>,
+    val: mir::ConstValue<'tcx>,
     ty: Ty<'tcx>,
 ) -> Option<mir::DestructuredConstant<'tcx>> {
     let param_env = ty::ParamEnv::reveal_all();
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 1675b824c52..2fba7455cb2 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -4,9 +4,10 @@ use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
 use crate::const_eval::CanAccessStatics;
 use crate::interpret::MPlaceTy;
 use crate::interpret::{
-    intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
-    MemoryKind, PlaceTy, Projectable, Scalar,
+    intern_const_alloc_recursive, ImmTy, Immediate, InternKind, MemPlaceMeta, MemoryKind, PlaceTy,
+    Projectable, Scalar,
 };
+use rustc_middle::mir;
 use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
 use rustc_span::source_map::DUMMY_SP;
@@ -206,7 +207,7 @@ pub fn valtree_to_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
     valtree: ty::ValTree<'tcx>,
-) -> ConstValue<'tcx> {
+) -> mir::ConstValue<'tcx> {
     // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
     // (those for constants with type bool, int, uint, float or char).
     // For all other types we create an `MPlace` and fill that by walking
@@ -219,10 +220,10 @@ pub fn valtree_to_const_value<'tcx>(
     match ty.kind() {
         ty::FnDef(..) => {
             assert!(valtree.unwrap_branch().is_empty());
-            ConstValue::ZeroSized
+            mir::ConstValue::ZeroSized
         }
         ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => match valtree {
-            ty::ValTree::Leaf(scalar_int) => ConstValue::Scalar(Scalar::Int(scalar_int)),
+            ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)),
             ty::ValTree::Branch(_) => bug!(
                 "ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
             ),
@@ -237,7 +238,7 @@ pub fn valtree_to_const_value<'tcx>(
             let layout = tcx.layout_of(param_env_ty).unwrap();
             if layout.is_zst() {
                 // Fast path to avoid some allocations.
-                return ConstValue::ZeroSized;
+                return mir::ConstValue::ZeroSized;
             }
             if layout.abi.is_scalar()
                 && (matches!(ty.kind(), ty::Tuple(_))
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 4c826239eca..bd897ffaafc 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -351,7 +351,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) {
             (&ty::Array(_, length), &ty::Slice(_)) => {
-                let ptr = self.read_scalar(src)?;
+                let ptr = self.read_pointer(src)?;
                 // u64 cast is from usize to u64, which is always good
                 let val = Immediate::new_slice(
                     ptr,
@@ -367,6 +367,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     return self.write_immediate(*val, dest);
                 }
                 let (old_data, old_vptr) = val.to_scalar_pair();
+                let old_data = old_data.to_pointer(self)?;
                 let old_vptr = old_vptr.to_pointer(self)?;
                 let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
                 if old_trait != data_a.principal() {
@@ -378,7 +379,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             (_, &ty::Dynamic(data, _, ty::Dyn)) => {
                 // Initial cast from sized to dyn trait
                 let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
-                let ptr = self.read_scalar(src)?;
+                let ptr = self.read_pointer(src)?;
                 let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
                 self.write_immediate(val, dest)
             }
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 3b58f66353b..775a834f2ee 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -5,10 +5,8 @@
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::{
     self,
-    interpret::{
-        Allocation, ConstAllocation, ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar,
-    },
-    BinOp, NonDivergingIntrinsic,
+    interpret::{Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar},
+    BinOp, ConstValue, NonDivergingIntrinsic,
 };
 use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
@@ -64,7 +62,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
         sym::type_name => {
             ensure_monomorphic_enough(tcx, tp_ty)?;
             let alloc = alloc_type_name(tcx, tp_ty);
-            ConstValue::Slice { data: alloc, start: 0, end: alloc.inner().len() }
+            ConstValue::Slice { data: alloc, meta: alloc.inner().size().bytes() }
         }
         sym::needs_drop => {
             ensure_monomorphic_enough(tcx, tp_ty)?;
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 082c0f5b84e..fdda98a50e8 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -13,9 +13,8 @@ use rustc_middle::{mir, ty};
 use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
 
 use super::{
-    alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, InterpCx,
-    InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, Projectable,
-    Provenance, Scalar,
+    alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
+    MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, Projectable, Provenance, Scalar,
 };
 
 /// An `Immediate` represents a single immediate self-contained Rust value.
@@ -44,24 +43,30 @@ impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
 }
 
 impl<Prov: Provenance> Immediate<Prov> {
-    pub fn from_pointer(p: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
-        Immediate::Scalar(Scalar::from_pointer(p, cx))
+    pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
+        Immediate::Scalar(Scalar::from_pointer(ptr, cx))
     }
 
-    pub fn from_maybe_pointer(p: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
-        Immediate::Scalar(Scalar::from_maybe_pointer(p, cx))
+    pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
+        Immediate::Scalar(Scalar::from_maybe_pointer(ptr, cx))
     }
 
-    pub fn new_slice(val: Scalar<Prov>, len: u64, cx: &impl HasDataLayout) -> Self {
-        Immediate::ScalarPair(val, Scalar::from_target_usize(len, cx))
+    pub fn new_slice(ptr: Pointer<Option<Prov>>, len: u64, cx: &impl HasDataLayout) -> Self {
+        Immediate::ScalarPair(
+            Scalar::from_maybe_pointer(ptr, cx),
+            Scalar::from_target_usize(len, cx),
+        )
     }
 
     pub fn new_dyn_trait(
-        val: Scalar<Prov>,
+        val: Pointer<Option<Prov>>,
         vtable: Pointer<Option<Prov>>,
         cx: &impl HasDataLayout,
     ) -> Self {
-        Immediate::ScalarPair(val, Scalar::from_maybe_pointer(vtable, cx))
+        Immediate::ScalarPair(
+            Scalar::from_maybe_pointer(val, cx),
+            Scalar::from_maybe_pointer(vtable, cx),
+        )
     }
 
     #[inline]
@@ -702,7 +707,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
     pub(crate) fn const_val_to_op(
         &self,
-        val_val: ConstValue<'tcx>,
+        val_val: mir::ConstValue<'tcx>,
         ty: Ty<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
@@ -715,24 +720,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         };
         let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
         let op = match val_val {
-            ConstValue::Indirect { alloc_id, offset } => {
+            mir::ConstValue::Indirect { alloc_id, offset } => {
                 // We rely on mutability being set correctly in that allocation to prevent writes
                 // where none should happen.
                 let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
                 Operand::Indirect(MemPlace::from_ptr(ptr.into()))
             }
-            ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
-            ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
-            ConstValue::Slice { data, start, end } => {
+            mir::ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
+            mir::ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
+            mir::ConstValue::Slice { data, meta } => {
                 // We rely on mutability being set correctly in `data` to prevent writes
                 // where none should happen.
-                let ptr = Pointer::new(
-                    self.tcx.reserve_and_set_memory_alloc(data),
-                    Size::from_bytes(start), // offset: `start`
-                );
+                let ptr = Pointer::new(self.tcx.reserve_and_set_memory_alloc(data), Size::ZERO);
                 Operand::Immediate(Immediate::new_slice(
-                    Scalar::from_pointer(self.global_base_pointer(ptr)?, &*self.tcx),
-                    u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start`
+                    self.global_base_pointer(ptr)?.into(),
+                    meta,
                     self,
                 ))
             }
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 0f66df5c30d..fb9aa9d3abe 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -9,16 +9,15 @@ use either::{Either, Left, Right};
 use rustc_ast::Mutability;
 use rustc_index::IndexSlice;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::PointerArithmetic;
 use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_middle::ty::Ty;
 use rustc_target::abi::{Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
 
 use super::{
-    alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
-    ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand,
-    Pointer, Projectable, Provenance, Readable, Scalar,
+    alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ImmTy,
+    Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, Pointer,
+    PointerArithmetic, Projectable, Provenance, Readable, Scalar,
 };
 
 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
@@ -1037,7 +1036,7 @@ where
 
     pub fn raw_const_to_mplace(
         &self,
-        raw: ConstAlloc<'tcx>,
+        raw: mir::ConstAlloc<'tcx>,
     ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
         // This must be an allocation in `tcx`
         let _ = self.tcx.global_alloc(raw.alloc_id);
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index bee5a89c4c7..7d037ddfa98 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -47,6 +47,8 @@ extern crate cfg_if;
 #[macro_use]
 extern crate rustc_macros;
 
+use std::fmt;
+
 pub use rustc_index::static_assert_size;
 
 #[inline(never)]
@@ -126,6 +128,23 @@ impl<F: FnOnce()> Drop for OnDrop<F> {
     }
 }
 
+/// Turns a closure that takes an `&mut Formatter` into something that can be display-formatted.
+pub fn make_display(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Display {
+    struct Printer<F> {
+        f: F,
+    }
+    impl<F> fmt::Display for Printer<F>
+    where
+        F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
+        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+            (self.f)(fmt)
+        }
+    }
+
+    Printer { f }
+}
+
 // See comments in src/librustc_middle/lib.rs
 #[doc(hidden)]
 pub fn __noop_fix_for_27438() {}
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index c02ae70166d..65c7aed3f10 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -7,7 +7,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(lazy_cell)]
 #![feature(decl_macro)]
-#![feature(ice_to_disk)]
+#![feature(panic_update_hook)]
 #![feature(let_chains)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
@@ -50,9 +50,9 @@ use std::collections::BTreeMap;
 use std::env;
 use std::ffi::OsString;
 use std::fmt::Write as _;
-use std::fs;
+use std::fs::{self, File};
 use std::io::{self, IsTerminal, Read, Write};
-use std::panic::{self, catch_unwind};
+use std::panic::{self, catch_unwind, PanicInfo};
 use std::path::PathBuf;
 use std::process::{self, Command, Stdio};
 use std::str;
@@ -1326,31 +1326,59 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
         std::env::set_var("RUST_BACKTRACE", "full");
     }
 
-    panic::set_hook(Box::new(move |info| {
-        // If the error was caused by a broken pipe then this is not a bug.
-        // Write the error and return immediately. See #98700.
-        #[cfg(windows)]
-        if let Some(msg) = info.payload().downcast_ref::<String>() {
-            if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") {
-                // the error code is already going to be reported when the panic unwinds up the stack
-                let handler = EarlyErrorHandler::new(ErrorOutputType::default());
-                let _ = handler.early_error_no_abort(msg.clone());
-                return;
-            }
-        };
-
-        // Invoke the default handler, which prints the actual panic message and optionally a backtrace
-        // Don't do this for delayed bugs, which already emit their own more useful backtrace.
-        if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
-            std::panic_hook_with_disk_dump(info, ice_path().as_deref());
+    panic::update_hook(Box::new(
+        move |default_hook: &(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static),
+              info: &PanicInfo<'_>| {
+            // If the error was caused by a broken pipe then this is not a bug.
+            // Write the error and return immediately. See #98700.
+            #[cfg(windows)]
+            if let Some(msg) = info.payload().downcast_ref::<String>() {
+                if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
+                {
+                    // the error code is already going to be reported when the panic unwinds up the stack
+                    let handler = EarlyErrorHandler::new(ErrorOutputType::default());
+                    let _ = handler.early_error_no_abort(msg.clone());
+                    return;
+                }
+            };
 
-            // Separate the output with an empty line
-            eprintln!();
-        }
+            // Invoke the default handler, which prints the actual panic message and optionally a backtrace
+            // Don't do this for delayed bugs, which already emit their own more useful backtrace.
+            if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
+                default_hook(info);
+                // Separate the output with an empty line
+                eprintln!();
+
+                if let Some(ice_path) = ice_path()
+                    && let Ok(mut out) =
+                        File::options().create(true).append(true).open(&ice_path)
+                {
+                    // The current implementation always returns `Some`.
+                    let location = info.location().unwrap();
+                    let msg = match info.payload().downcast_ref::<&'static str>() {
+                        Some(s) => *s,
+                        None => match info.payload().downcast_ref::<String>() {
+                            Some(s) => &s[..],
+                            None => "Box<dyn Any>",
+                        },
+                    };
+                    let thread = std::thread::current();
+                    let name = thread.name().unwrap_or("<unnamed>");
+                    let _ = write!(
+                        &mut out,
+                        "thread '{name}' panicked at {location}:\n\
+                        {msg}\n\
+                        stack backtrace:\n\
+                        {:#}",
+                        std::backtrace::Backtrace::force_capture()
+                    );
+                }
+            }
 
-        // Print the ICE message
-        report_ice(info, bug_report_url, extra_info);
-    }));
+            // Print the ICE message
+            report_ice(info, bug_report_url, extra_info);
+        },
+    ));
 }
 
 /// Prints the ICE message, including query stack, but without backtrace.
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index 66b153d8931..168b336e374 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -278,7 +278,8 @@ pub enum DefPathData {
     Ctor,
     /// A constant expression (see `{ast,hir}::AnonConst`).
     AnonConst,
-    /// An `impl Trait` type node.
+    /// An existential `impl Trait` type node.
+    /// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
     ImplTrait,
     /// `impl Trait` generated associated type node.
     ImplTraitAssocTy,
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 18f7a18625e..1160290cdff 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -21,12 +21,16 @@ hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit wh
     .label = deref recursion limit reached
     .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`)
 
-hir_analysis_cannot_capture_late_bound_const_in_anon_const =
-    cannot capture late-bound const parameter in a constant
+hir_analysis_cannot_capture_late_bound_const =
+    cannot capture late-bound const parameter in {$what}
     .label = parameter defined here
 
-hir_analysis_cannot_capture_late_bound_ty_in_anon_const =
-    cannot capture late-bound type parameter in a constant
+hir_analysis_cannot_capture_late_bound_lifetime =
+    cannot capture late-bound lifetime in {$what}
+    .label = lifetime defined here
+
+hir_analysis_cannot_capture_late_bound_ty =
+    cannot capture late-bound type parameter in {$what}
     .label = parameter defined here
 
 hir_analysis_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_ty}` to fat pointer `{$cast_ty}`
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index acfd8dcb112..3a97a383ee1 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -268,9 +268,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 // (*) -- not late-bound, won't change
             }
 
-            Some(rbv::ResolvedArg::Error(_)) => {
-                bug!("only ty/ct should resolve as ResolvedArg::Error")
-            }
+            Some(rbv::ResolvedArg::Error(guar)) => ty::Region::new_error(tcx, guar),
 
             None => {
                 self.re_infer(def, lifetime.ident.span).unwrap_or_else(|| {
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 92cc9759304..38f19aa09ad 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -18,7 +18,7 @@ use rustc_middle::ty::util::ExplicitSelf;
 use rustc_middle::ty::{
     self, GenericArgs, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
 };
-use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
+use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
@@ -2196,16 +2196,16 @@ pub(super) fn check_type_bounds<'tcx>(
                 //
                 // impl<T> X for T where T: X { type Y = <T as X>::Y; }
             }
-            _ => predicates.push(
+            _ => predicates.push(ty::Clause::from_projection_clause(
+                tcx,
                 ty::Binder::bind_with_vars(
                     ty::ProjectionPredicate {
                         projection_ty: tcx.mk_alias_ty(trait_ty.def_id, rebased_args),
                         term: normalize_impl_ty.into(),
                     },
                     bound_vars,
-                )
-                .to_predicate(tcx),
-            ),
+                ),
+            )),
         };
         ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing)
     };
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 2bee2727725..6869c869603 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -158,13 +158,14 @@ enum Scope<'a> {
         s: ScopeRef<'a>,
     },
 
-    /// Disallows capturing non-lifetime binders from parent scopes.
+    /// Disallows capturing late-bound vars from parent scopes.
     ///
     /// This is necessary for something like `for<T> [(); { /* references T */ }]:`,
     /// since we don't do something more correct like replacing any captured
     /// late-bound vars with early-bound params in the const's own generics.
-    AnonConstBoundary {
+    LateBoundary {
         s: ScopeRef<'a>,
+        what: &'static str,
     },
 
     Root {
@@ -216,7 +217,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 .field("s", &"..")
                 .finish(),
             Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(),
-            Scope::AnonConstBoundary { s: _ } => f.debug_struct("AnonConstBoundary").finish(),
+            Scope::LateBoundary { s: _, what } => {
+                f.debug_struct("LateBoundary").field("what", what).finish()
+            }
             Scope::Root { opt_parent_item } => {
                 f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish()
             }
@@ -318,7 +321,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     break (vec![], BinderScopeType::Normal);
                 }
 
-                Scope::ObjectLifetimeDefault { s, .. } | Scope::AnonConstBoundary { s } => {
+                Scope::ObjectLifetimeDefault { s, .. } | Scope::LateBoundary { s, .. } => {
                     scope = s;
                 }
 
@@ -697,9 +700,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     }) => {
                         intravisit::walk_ty(self, ty);
 
-                        // Elided lifetimes are not allowed in non-return
-                        // position impl Trait
-                        let scope = Scope::TraitRefBoundary { s: self.scope };
+                        // Elided lifetimes and late-bound lifetimes (from the parent)
+                        // are not allowed in non-return position impl Trait
+                        let scope = Scope::LateBoundary {
+                            s: &Scope::TraitRefBoundary { s: self.scope },
+                            what: "type alias impl trait",
+                        };
                         self.with(scope, |this| intravisit::walk_item(this, opaque_ty));
 
                         return;
@@ -979,7 +985,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
     }
 
     fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
-        self.with(Scope::AnonConstBoundary { s: self.scope }, |this| {
+        self.with(Scope::LateBoundary { s: self.scope, what: "constant" }, |this| {
             intravisit::walk_anon_const(this, c);
         });
     }
@@ -1174,6 +1180,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         let mut late_depth = 0;
         let mut scope = self.scope;
         let mut outermost_body = None;
+        let mut crossed_late_boundary = None;
         let result = loop {
             match *scope {
                 Scope::Body { id, s } => {
@@ -1258,8 +1265,12 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
 
                 Scope::ObjectLifetimeDefault { s, .. }
                 | Scope::Supertrait { s, .. }
-                | Scope::TraitRefBoundary { s, .. }
-                | Scope::AnonConstBoundary { s } => {
+                | Scope::TraitRefBoundary { s, .. } => {
+                    scope = s;
+                }
+
+                Scope::LateBoundary { s, what } => {
+                    crossed_late_boundary = Some(what);
                     scope = s;
                 }
             }
@@ -1268,6 +1279,22 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         if let Some(mut def) = result {
             if let ResolvedArg::EarlyBound(..) = def {
                 // Do not free early-bound regions, only late-bound ones.
+            } else if let ResolvedArg::LateBound(_, _, param_def_id) = def
+                && let Some(what) = crossed_late_boundary
+            {
+                let use_span = lifetime_ref.ident.span;
+                let def_span = self.tcx.def_span(param_def_id);
+                let guar = match self.tcx.def_kind(param_def_id) {
+                    DefKind::LifetimeParam => {
+                        self.tcx.sess.emit_err(errors::CannotCaptureLateBound::Lifetime {
+                            use_span,
+                            def_span,
+                            what,
+                        })
+                    }
+                    _ => unreachable!(),
+                };
+                def = ResolvedArg::Error(guar);
             } else if let Some(body_id) = outermost_body {
                 let fn_id = self.tcx.hir().body_owner(body_id);
                 match self.tcx.hir().get(fn_id) {
@@ -1322,7 +1349,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 | Scope::ObjectLifetimeDefault { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. }
-                | Scope::AnonConstBoundary { s } => {
+                | Scope::LateBoundary { s, .. } => {
                     scope = s;
                 }
             }
@@ -1341,7 +1368,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         // search.
         let mut late_depth = 0;
         let mut scope = self.scope;
-        let mut crossed_anon_const = false;
+        let mut crossed_late_boundary = None;
 
         let result = loop {
             match *scope {
@@ -1376,28 +1403,32 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     scope = s;
                 }
 
-                Scope::AnonConstBoundary { s } => {
-                    crossed_anon_const = true;
+                Scope::LateBoundary { s, what } => {
+                    crossed_late_boundary = Some(what);
                     scope = s;
                 }
             }
         };
 
         if let Some(def) = result {
-            if let ResolvedArg::LateBound(..) = def && crossed_anon_const {
+            if let ResolvedArg::LateBound(..) = def
+                && let Some(what) = crossed_late_boundary
+            {
                 let use_span = self.tcx.hir().span(hir_id);
                 let def_span = self.tcx.def_span(param_def_id);
                 let guar = match self.tcx.def_kind(param_def_id) {
                     DefKind::ConstParam => {
-                        self.tcx.sess.emit_err(errors::CannotCaptureLateBoundInAnonConst::Const {
+                        self.tcx.sess.emit_err(errors::CannotCaptureLateBound::Const {
                             use_span,
                             def_span,
+                            what,
                         })
                     }
                     DefKind::TyParam => {
-                        self.tcx.sess.emit_err(errors::CannotCaptureLateBoundInAnonConst::Type {
+                        self.tcx.sess.emit_err(errors::CannotCaptureLateBound::Type {
                             use_span,
                             def_span,
+                            what,
                         })
                     }
                     _ => unreachable!(),
@@ -1446,7 +1477,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                 | Scope::ObjectLifetimeDefault { s, .. }
                 | Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. }
-                | Scope::AnonConstBoundary { s } => {
+                | Scope::LateBoundary { s, .. } => {
                     scope = s;
                 }
             }
@@ -1526,7 +1557,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                         | Scope::ObjectLifetimeDefault { s, .. }
                         | Scope::Supertrait { s, .. }
                         | Scope::TraitRefBoundary { s, .. }
-                        | Scope::AnonConstBoundary { s } => {
+                        | Scope::LateBoundary { s, .. } => {
                             scope = s;
                         }
                     }
@@ -1831,7 +1862,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
 
                 Scope::Supertrait { s, .. }
                 | Scope::TraitRefBoundary { s, .. }
-                | Scope::AnonConstBoundary { s } => {
+                | Scope::LateBoundary { s, .. } => {
                     scope = s;
                 }
             }
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index b83ef8d07db..4705e40988b 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -430,20 +430,30 @@ pub(crate) struct VariadicFunctionCompatibleConvention<'a> {
 }
 
 #[derive(Diagnostic)]
-pub(crate) enum CannotCaptureLateBoundInAnonConst {
-    #[diag(hir_analysis_cannot_capture_late_bound_ty_in_anon_const)]
+pub(crate) enum CannotCaptureLateBound {
+    #[diag(hir_analysis_cannot_capture_late_bound_ty)]
     Type {
         #[primary_span]
         use_span: Span,
         #[label]
         def_span: Span,
+        what: &'static str,
     },
-    #[diag(hir_analysis_cannot_capture_late_bound_const_in_anon_const)]
+    #[diag(hir_analysis_cannot_capture_late_bound_const)]
     Const {
         #[primary_span]
         use_span: Span,
         #[label]
         def_span: Span,
+        what: &'static str,
+    },
+    #[diag(hir_analysis_cannot_capture_late_bound_lifetime)]
+    Lifetime {
+        #[primary_span]
+        use_span: Span,
+        #[label]
+        def_span: Span,
+        what: &'static str,
     },
 }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 2acb43c51da..43d4496dd48 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -33,27 +33,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         let generics = self.tcx.generics_of(def_id);
-        let predicate_args = match unsubstituted_pred.kind().skip_binder() {
-            ty::ClauseKind::Trait(pred) => pred.trait_ref.args.to_vec(),
-            ty::ClauseKind::Projection(pred) => pred.projection_ty.args.to_vec(),
-            ty::ClauseKind::ConstArgHasType(arg, ty) => {
-                vec![ty.into(), arg.into()]
-            }
-            ty::ClauseKind::ConstEvaluatable(e) => vec![e.into()],
-            _ => return false,
-        };
+        let (predicate_args, predicate_self_type_to_point_at) =
+            match unsubstituted_pred.kind().skip_binder() {
+                ty::ClauseKind::Trait(pred) => {
+                    (pred.trait_ref.args.to_vec(), Some(pred.self_ty().into()))
+                }
+                ty::ClauseKind::Projection(pred) => (pred.projection_ty.args.to_vec(), None),
+                ty::ClauseKind::ConstArgHasType(arg, ty) => (vec![ty.into(), arg.into()], None),
+                ty::ClauseKind::ConstEvaluatable(e) => (vec![e.into()], None),
+                _ => return false,
+            };
 
-        let direct_param = if let ty::ClauseKind::Trait(pred) = unsubstituted_pred.kind().skip_binder()
-            && let ty = pred.trait_ref.self_ty()
-            && let ty::Param(_param) = ty.kind()
-            && let Some(arg) = predicate_args.get(0)
-            && let ty::GenericArgKind::Type(arg_ty) = arg.unpack()
-            && arg_ty == ty
-        {
-            Some(*arg)
-        } else {
-            None
-        };
         let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| {
             predicate_args.iter().find_map(|arg| {
                 arg.walk().find_map(|arg| {
@@ -112,18 +102,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let qpath =
                     if let hir::ExprKind::Path(qpath) = expr.kind { Some(qpath) } else { None };
 
-                (Some(*expr), qpath)
+                (Some(&expr.kind), qpath)
             }
             hir::Node::Ty(hir::Ty { kind: hir::TyKind::Path(qpath), .. }) => (None, Some(*qpath)),
             _ => return false,
         };
 
         if let Some(qpath) = qpath {
-            if let Some(param) = direct_param {
-                if self.point_at_path_if_possible(error, def_id, param, &qpath) {
-                    return true;
-                }
+            // Prefer pointing at the turbofished arg that corresponds to the
+            // self type of the failing predicate over anything else.
+            if let Some(param) = predicate_self_type_to_point_at
+                && self.point_at_path_if_possible(error, def_id, param, &qpath)
+            {
+                return true;
             }
+
             if let hir::Node::Expr(hir::Expr {
                 kind: hir::ExprKind::Call(callee, args),
                 hir_id: call_hir_id,
@@ -166,11 +159,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        match expr.map(|e| e.kind) {
+        match expr {
             Some(hir::ExprKind::MethodCall(segment, receiver, args, ..)) => {
-                if let Some(param) = direct_param
+                if let Some(param) = predicate_self_type_to_point_at
                     && self.point_at_generic_if_possible(error, def_id, param, segment)
                 {
+                    // HACK: This is not correct, since `predicate_self_type_to_point_at` might
+                    // not actually correspond to the receiver of the method call. But we
+                    // re-adjust the cause code here in order to prefer pointing at one of
+                    // the method's turbofish segments but still use `FunctionArgumentObligation`
+                    // elsewhere. Hopefully this doesn't break something.
                     error.obligation.cause.map_code(|parent_code| {
                         ObligationCauseCode::FunctionArgumentObligation {
                             arg_hir_id: receiver.hir_id,
@@ -180,6 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     });
                     return true;
                 }
+
                 for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
                     .into_iter()
                     .flatten()
@@ -237,7 +236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 for param in [
-                    direct_param,
+                    predicate_self_type_to_point_at,
                     param_to_point_at,
                     fallback_param_to_point_at,
                     self_param_to_point_at,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 4237b4488ca..4a245d30c8e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -325,13 +325,16 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
     fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) {
         // FIXME: normalization and escaping regions
         let ty = if !ty.has_escaping_bound_vars() {
-            if let ty::Alias(
-                ty::AliasKind::Projection | ty::AliasKind::Weak,
-                ty::AliasTy { args, def_id, .. },
-            ) = ty.kind()
+            // NOTE: These obligations are 100% redundant and are implied by
+            // WF obligations that are registered elsewhere, but they have a
+            // better cause code assigned to them in `add_required_obligations_for_hir`.
+            // This means that they should shadow obligations with worse spans.
+            if let ty::Alias(ty::Projection | ty::Weak, ty::AliasTy { args, def_id, .. }) =
+                ty.kind()
             {
                 self.add_required_obligations_for_hir(span, *def_id, args, hir_id);
             }
+
             self.normalize(span, ty)
         } else {
             ty
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index e906c906347..35987072ed6 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -5,7 +5,6 @@ use rustc_hir::def::{CtorKind, CtorOf};
 use rustc_index::Idx;
 use rustc_middle::ty::{ParameterizedOverTcx, UnusedGenericParams};
 use rustc_serialize::opaque::FileEncoder;
-use rustc_serialize::Encoder as _;
 use rustc_span::hygiene::MacroKind;
 use std::marker::PhantomData;
 use std::num::NonZeroUsize;
@@ -468,7 +467,10 @@ impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBui
 
         let width = self.width;
         for block in &self.blocks {
-            buf.emit_raw_bytes(&block[..width]);
+            buf.write_with(|dest| {
+                *dest = *block;
+                width
+            });
         }
 
         LazyTable::from_position_and_encoded_size(
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
new file mode 100644
index 00000000000..0115b1dcb3e
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -0,0 +1,583 @@
+use std::fmt::{self, Debug, Display, Formatter};
+
+use rustc_hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::{self as hir};
+use rustc_span::Span;
+use rustc_target::abi::{HasDataLayout, Size};
+
+use crate::mir::interpret::{
+    alloc_range, AllocId, ConstAllocation, ErrorHandled, GlobalAlloc, Scalar,
+};
+use crate::mir::{pretty_print_const_value, Promoted};
+use crate::ty::{self, print::pretty_print_const, List, Ty, TyCtxt};
+use crate::ty::{GenericArgs, GenericArgsRef};
+use crate::ty::{ScalarInt, UserTypeAnnotationIndex};
+
+///////////////////////////////////////////////////////////////////////////
+/// Evaluated Constants
+
+/// Represents the result of const evaluation via the `eval_to_allocation` query.
+/// Not to be confused with `ConstAllocation`, which directly refers to the underlying data!
+/// Here we indirect via an `AllocId`.
+#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
+pub struct ConstAlloc<'tcx> {
+    /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
+    /// (so you can use `AllocMap::unwrap_memory`).
+    pub alloc_id: AllocId,
+    pub ty: Ty<'tcx>,
+}
+
+/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
+/// array length computations, enum discriminants and the pattern matching logic.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
+#[derive(HashStable, Lift)]
+pub enum ConstValue<'tcx> {
+    /// Used for types with `layout::abi::Scalar` ABI.
+    ///
+    /// Not using the enum `Value` to encode that this must not be `Uninit`.
+    Scalar(Scalar),
+
+    /// Only for ZSTs.
+    ZeroSized,
+
+    /// Used for references to unsized types with slice tail.
+    ///
+    /// This is worth an optimized representation since Rust has literals of type `&str` and
+    /// `&[u8]`. Not having to indirect those through an `AllocId` (or two, if we used `Indirect`)
+    /// has shown measurable performance improvements on stress tests. We then reuse this
+    /// optimization for slice-tail types more generally during valtree-to-constval conversion.
+    Slice {
+        /// The allocation storing the slice contents.
+        /// This always points to the beginning of the allocation.
+        data: ConstAllocation<'tcx>,
+        /// The metadata field of the reference.
+        /// This is a "target usize", so we use `u64` as in the interpreter.
+        meta: u64,
+    },
+
+    /// A value not representable by the other variants; needs to be stored in-memory.
+    ///
+    /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
+    Indirect {
+        /// The backing memory of the value. May contain more memory than needed for just the value
+        /// if this points into some other larger ConstValue.
+        ///
+        /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
+        /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
+        /// back, we can preserve the original `AllocId`.
+        alloc_id: AllocId,
+        /// Offset into `alloc`
+        offset: Size,
+    },
+}
+
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+static_assert_size!(ConstValue<'_>, 24);
+
+impl<'tcx> ConstValue<'tcx> {
+    #[inline]
+    pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
+        match *self {
+            ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
+            ConstValue::Scalar(val) => Some(val),
+        }
+    }
+
+    pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
+        self.try_to_scalar()?.try_to_int().ok()
+    }
+
+    pub fn try_to_bits(&self, size: Size) -> Option<u128> {
+        self.try_to_scalar_int()?.to_bits(size).ok()
+    }
+
+    pub fn try_to_bool(&self) -> Option<bool> {
+        self.try_to_scalar_int()?.try_into().ok()
+    }
+
+    pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
+        self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
+    }
+
+    pub fn try_to_bits_for_ty(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Option<u128> {
+        let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
+        self.try_to_bits(size)
+    }
+
+    pub fn from_bool(b: bool) -> Self {
+        ConstValue::Scalar(Scalar::from_bool(b))
+    }
+
+    pub fn from_u64(i: u64) -> Self {
+        ConstValue::Scalar(Scalar::from_u64(i))
+    }
+
+    pub fn from_u128(i: u128) -> Self {
+        ConstValue::Scalar(Scalar::from_u128(i))
+    }
+
+    pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
+        ConstValue::Scalar(Scalar::from_target_usize(i, cx))
+    }
+
+    /// Must only be called on constants of type `&str` or `&[u8]`!
+    pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
+        let (data, start, end) = match self {
+            ConstValue::Scalar(_) | ConstValue::ZeroSized => {
+                bug!("`try_get_slice_bytes` on non-slice constant")
+            }
+            &ConstValue::Slice { data, meta } => (data, 0, meta),
+            &ConstValue::Indirect { alloc_id, offset } => {
+                // The reference itself is stored behind an indirection.
+                // Load the reference, and then load the actual slice contents.
+                let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
+                let ptr_size = tcx.data_layout.pointer_size;
+                if a.size() < offset + 2 * ptr_size {
+                    // (partially) dangling reference
+                    return None;
+                }
+                // Read the wide pointer components.
+                let ptr = a
+                    .read_scalar(
+                        &tcx,
+                        alloc_range(offset, ptr_size),
+                        /* read_provenance */ true,
+                    )
+                    .ok()?;
+                let ptr = ptr.to_pointer(&tcx).ok()?;
+                let len = a
+                    .read_scalar(
+                        &tcx,
+                        alloc_range(offset + ptr_size, ptr_size),
+                        /* read_provenance */ false,
+                    )
+                    .ok()?;
+                let len = len.to_target_usize(&tcx).ok()?;
+                if len == 0 {
+                    return Some(&[]);
+                }
+                // Non-empty slice, must have memory. We know this is a relative pointer.
+                let (inner_alloc_id, offset) = ptr.into_parts();
+                let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
+                (data, offset.bytes(), offset.bytes() + len)
+            }
+        };
+
+        // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
+        let start = start.try_into().unwrap();
+        let end = end.try_into().unwrap();
+        Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// Constants
+///
+/// Two constants are equal if they are the same constant. Note that
+/// this does not necessarily mean that they are `==` in Rust. In
+/// particular, one must be wary of `NaN`!
+
+#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub struct Constant<'tcx> {
+    pub span: Span,
+
+    /// Optional user-given type: for something like
+    /// `collect::<Vec<_>>`, this would be present and would
+    /// indicate that `Vec<_>` was explicitly specified.
+    ///
+    /// Needed for NLL to impose user-given type constraints.
+    pub user_ty: Option<UserTypeAnnotationIndex>,
+
+    pub literal: ConstantKind<'tcx>,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum ConstantKind<'tcx> {
+    /// This constant came from the type system.
+    ///
+    /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
+    /// this ensures that we consistently produce "clean" values without data in the padding or
+    /// anything like that.
+    Ty(ty::Const<'tcx>),
+
+    /// An unevaluated mir constant which is not part of the type system.
+    ///
+    /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will
+    /// always flow through a valtree, so all data not captured in the valtree is lost. This variant
+    /// directly uses the evaluated result of the given constant, including e.g. data stored in
+    /// padding.
+    Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
+
+    /// This constant cannot go back into the type system, as it represents
+    /// something the type system cannot handle (e.g. pointers).
+    Val(ConstValue<'tcx>, Ty<'tcx>),
+}
+
+impl<'tcx> Constant<'tcx> {
+    pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
+        match self.literal.try_to_scalar() {
+            Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
+                GlobalAlloc::Static(def_id) => {
+                    assert!(!tcx.is_thread_local_static(def_id));
+                    Some(def_id)
+                }
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+    #[inline]
+    pub fn ty(&self) -> Ty<'tcx> {
+        self.literal.ty()
+    }
+}
+
+impl<'tcx> ConstantKind<'tcx> {
+    #[inline(always)]
+    pub fn ty(&self) -> Ty<'tcx> {
+        match self {
+            ConstantKind::Ty(c) => c.ty(),
+            ConstantKind::Val(_, ty) | ConstantKind::Unevaluated(_, ty) => *ty,
+        }
+    }
+
+    #[inline]
+    pub fn try_to_scalar(self) -> Option<Scalar> {
+        match self {
+            ConstantKind::Ty(c) => match c.kind() {
+                ty::ConstKind::Value(valtree) => match valtree {
+                    ty::ValTree::Leaf(scalar_int) => Some(Scalar::Int(scalar_int)),
+                    ty::ValTree::Branch(_) => None,
+                },
+                _ => None,
+            },
+            ConstantKind::Val(val, _) => val.try_to_scalar(),
+            ConstantKind::Unevaluated(..) => None,
+        }
+    }
+
+    #[inline]
+    pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
+        self.try_to_scalar()?.try_to_int().ok()
+    }
+
+    #[inline]
+    pub fn try_to_bits(self, size: Size) -> Option<u128> {
+        self.try_to_scalar_int()?.to_bits(size).ok()
+    }
+
+    #[inline]
+    pub fn try_to_bool(self) -> Option<bool> {
+        self.try_to_scalar_int()?.try_into().ok()
+    }
+
+    #[inline]
+    pub fn eval(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        span: Option<Span>,
+    ) -> Result<ConstValue<'tcx>, ErrorHandled> {
+        match self {
+            ConstantKind::Ty(c) => {
+                // We want to consistently have a "clean" value for type system constants (i.e., no
+                // data hidden in the padding), so we always go through a valtree here.
+                let val = c.eval(tcx, param_env, span)?;
+                Ok(tcx.valtree_to_const_val((self.ty(), val)))
+            }
+            ConstantKind::Unevaluated(uneval, _) => {
+                // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
+                tcx.const_eval_resolve(param_env, uneval, span)
+            }
+            ConstantKind::Val(val, _) => Ok(val),
+        }
+    }
+
+    /// Normalizes the constant to a value or an error if possible.
+    #[inline]
+    pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
+        match self.eval(tcx, param_env, None) {
+            Ok(val) => Self::Val(val, self.ty()),
+            Err(ErrorHandled::Reported(guar, _span)) => {
+                Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
+            }
+            Err(ErrorHandled::TooGeneric(_span)) => self,
+        }
+    }
+
+    #[inline]
+    pub fn try_eval_scalar(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<Scalar> {
+        self.eval(tcx, param_env, None).ok()?.try_to_scalar()
+    }
+
+    #[inline]
+    pub fn try_eval_scalar_int(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<ScalarInt> {
+        self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
+    }
+
+    #[inline]
+    pub fn try_eval_bits(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Option<u128> {
+        let int = self.try_eval_scalar_int(tcx, param_env)?;
+        assert_eq!(self.ty(), ty);
+        let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
+        int.to_bits(size).ok()
+    }
+
+    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
+    #[inline]
+    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
+        self.try_eval_bits(tcx, param_env, ty)
+            .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
+    }
+
+    #[inline]
+    pub fn try_eval_target_usize(
+        self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Option<u64> {
+        self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
+    }
+
+    #[inline]
+    /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
+    pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u64 {
+        self.try_eval_target_usize(tcx, param_env)
+            .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
+    }
+
+    #[inline]
+    pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
+        self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
+    }
+
+    #[inline]
+    pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
+        Self::Val(val, ty)
+    }
+
+    pub fn from_bits(
+        tcx: TyCtxt<'tcx>,
+        bits: u128,
+        param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+    ) -> Self {
+        let size = tcx
+            .layout_of(param_env_ty)
+            .unwrap_or_else(|e| {
+                bug!("could not compute layout for {:?}: {:?}", param_env_ty.value, e)
+            })
+            .size;
+        let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
+
+        Self::Val(cv, param_env_ty.value)
+    }
+
+    #[inline]
+    pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
+        let cv = ConstValue::from_bool(v);
+        Self::Val(cv, tcx.types.bool)
+    }
+
+    #[inline]
+    pub fn zero_sized(ty: Ty<'tcx>) -> Self {
+        let cv = ConstValue::ZeroSized;
+        Self::Val(cv, ty)
+    }
+
+    pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
+        let ty = tcx.types.usize;
+        Self::from_bits(tcx, n as u128, ty::ParamEnv::empty().and(ty))
+    }
+
+    #[inline]
+    pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
+        let val = ConstValue::Scalar(s);
+        Self::Val(val, ty)
+    }
+
+    /// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly
+    /// converted to a constant, everything else becomes `Unevaluated`.
+    #[instrument(skip(tcx), level = "debug", ret)]
+    pub fn from_anon_const(
+        tcx: TyCtxt<'tcx>,
+        def: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Self {
+        let body_id = match tcx.hir().get_by_def_id(def) {
+            hir::Node::AnonConst(ac) => ac.body,
+            _ => {
+                span_bug!(tcx.def_span(def), "from_anon_const can only process anonymous constants")
+            }
+        };
+
+        let expr = &tcx.hir().body(body_id).value;
+        debug!(?expr);
+
+        // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
+        // currently have to be wrapped in curly brackets, so it's necessary to special-case.
+        let expr = match &expr.kind {
+            hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
+                block.expr.as_ref().unwrap()
+            }
+            _ => expr,
+        };
+        debug!("expr.kind: {:?}", expr.kind);
+
+        let ty = tcx.type_of(def).instantiate_identity();
+        debug!(?ty);
+
+        // FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
+        // does not provide the parents generics to anonymous constants. We still allow generic const
+        // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
+        // ever try to substitute the generic parameters in their bodies.
+        //
+        // While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
+        // cause issues if we were to remove that special-case and try to evaluate the constant instead.
+        use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
+        match expr.kind {
+            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 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.item_name(def_id);
+                let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
+                debug!(?ty_const);
+
+                return Self::Ty(ty_const);
+            }
+            _ => {}
+        }
+
+        let hir_id = tcx.hir().local_def_id_to_hir_id(def);
+        let parent_args = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id)
+            && let Some(parent_did) = parent_hir_id.as_owner()
+        {
+            GenericArgs::identity_for_item(tcx, parent_did)
+        } else {
+            List::empty()
+        };
+        debug!(?parent_args);
+
+        let did = def.to_def_id();
+        let child_args = GenericArgs::identity_for_item(tcx, did);
+        let args = tcx.mk_args_from_iter(parent_args.into_iter().chain(child_args.into_iter()));
+        debug!(?args);
+
+        let span = tcx.def_span(def);
+        let uneval = UnevaluatedConst::new(did, args);
+        debug!(?span, ?param_env);
+
+        match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
+            Ok(val) => {
+                debug!("evaluated const value");
+                Self::Val(val, ty)
+            }
+            Err(_) => {
+                debug!("error encountered during evaluation");
+                // Error was handled in `const_eval_resolve`. Here we just create a
+                // new unevaluated const and error hard later in codegen
+                Self::Unevaluated(
+                    UnevaluatedConst {
+                        def: did,
+                        args: GenericArgs::identity_for_item(tcx, did),
+                        promoted: None,
+                    },
+                    ty,
+                )
+            }
+        }
+    }
+
+    pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+        match c.kind() {
+            ty::ConstKind::Value(valtree) => {
+                // Make sure that if `c` is normalized, then the return value is normalized.
+                let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
+                Self::Val(const_val, c.ty())
+            }
+            _ => Self::Ty(c),
+        }
+    }
+}
+
+/// An unevaluated (potentially generic) constant used in MIR.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
+pub struct UnevaluatedConst<'tcx> {
+    pub def: DefId,
+    pub args: GenericArgsRef<'tcx>,
+    pub promoted: Option<Promoted>,
+}
+
+impl<'tcx> UnevaluatedConst<'tcx> {
+    #[inline]
+    pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
+        assert_eq!(self.promoted, None);
+        ty::UnevaluatedConst { def: self.def, args: self.args }
+    }
+}
+
+impl<'tcx> UnevaluatedConst<'tcx> {
+    #[inline]
+    pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
+        UnevaluatedConst { def, args, promoted: Default::default() }
+    }
+
+    #[inline]
+    pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
+        UnevaluatedConst::new(instance.def_id(), instance.args)
+    }
+}
+
+impl<'tcx> Debug for Constant<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        write!(fmt, "{self}")
+    }
+}
+
+impl<'tcx> Display for Constant<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        match self.ty().kind() {
+            ty::FnDef(..) => {}
+            _ => write!(fmt, "const ")?,
+        }
+        Display::fmt(&self.literal, fmt)
+    }
+}
+
+impl<'tcx> Display for ConstantKind<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        match *self {
+            ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
+            ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
+            // FIXME(valtrees): Correctly print mir constants.
+            ConstantKind::Unevaluated(..) => {
+                fmt.write_str("_")?;
+                Ok(())
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index eb4614745d5..bc464aca5f3 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -1,7 +1,7 @@
-use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar};
+use super::{AllocId, AllocRange, Pointer, Scalar};
 
 use crate::error;
-use crate::mir::interpret::ConstValue;
+use crate::mir::{ConstAlloc, ConstValue};
 use crate::query::TyCtxtAt;
 use crate::ty::{layout, tls, Ty, TyCtxt, ValTree};
 
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 8c00746a180..d21f82f04f6 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -149,7 +149,7 @@ pub use self::error::{
     UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
 };
 
-pub use self::value::{ConstAlloc, ConstValue, Scalar};
+pub use self::value::Scalar;
 
 pub use self::allocation::{
     alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation,
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index c169733ad74..0d548f88636 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -9,163 +9,9 @@ use rustc_apfloat::{
 use rustc_macros::HashStable;
 use rustc_target::abi::{HasDataLayout, Size};
 
-use crate::{
-    mir::interpret::alloc_range,
-    ty::{ParamEnv, ScalarInt, Ty, TyCtxt},
-};
-
-use super::{
-    AllocId, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
-    ScalarSizeMismatch,
-};
-
-/// Represents the result of const evaluation via the `eval_to_allocation` query.
-#[derive(Copy, Clone, HashStable, TyEncodable, TyDecodable, Debug, Hash, Eq, PartialEq)]
-pub struct ConstAlloc<'tcx> {
-    /// The value lives here, at offset 0, and that allocation definitely is an `AllocKind::Memory`
-    /// (so you can use `AllocMap::unwrap_memory`).
-    pub alloc_id: AllocId,
-    pub ty: Ty<'tcx>,
-}
-
-/// Represents a constant value in Rust. `Scalar` and `Slice` are optimizations for
-/// array length computations, enum discriminants and the pattern matching logic.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
-#[derive(HashStable, Lift)]
-pub enum ConstValue<'tcx> {
-    /// Used for types with `layout::abi::Scalar` ABI.
-    ///
-    /// Not using the enum `Value` to encode that this must not be `Uninit`.
-    Scalar(Scalar),
+use crate::ty::ScalarInt;
 
-    /// Only for ZSTs.
-    ZeroSized,
-
-    /// Used for `&[u8]` and `&str`.
-    ///
-    /// This is worth an optimized representation since Rust has literals of these types.
-    /// Not having to indirect those through an `AllocId` (or two, if we used `Indirect`) has shown
-    /// measurable performance improvements on stress tests.
-    Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
-
-    /// A value not representable by the other variants; needs to be stored in-memory.
-    ///
-    /// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
-    Indirect {
-        /// The backing memory of the value. May contain more memory than needed for just the value
-        /// if this points into some other larger ConstValue.
-        ///
-        /// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
-        /// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
-        /// back, we can preserve the original `AllocId`.
-        alloc_id: AllocId,
-        /// Offset into `alloc`
-        offset: Size,
-    },
-}
-
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(ConstValue<'_>, 32);
-
-impl<'tcx> ConstValue<'tcx> {
-    #[inline]
-    pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
-        match *self {
-            ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
-            ConstValue::Scalar(val) => Some(val),
-        }
-    }
-
-    pub fn try_to_scalar_int(&self) -> Option<ScalarInt> {
-        self.try_to_scalar()?.try_to_int().ok()
-    }
-
-    pub fn try_to_bits(&self, size: Size) -> Option<u128> {
-        self.try_to_scalar_int()?.to_bits(size).ok()
-    }
-
-    pub fn try_to_bool(&self) -> Option<bool> {
-        self.try_to_scalar_int()?.try_into().ok()
-    }
-
-    pub fn try_to_target_usize(&self, tcx: TyCtxt<'tcx>) -> Option<u64> {
-        self.try_to_scalar_int()?.try_to_target_usize(tcx).ok()
-    }
-
-    pub fn try_to_bits_for_ty(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ParamEnv<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Option<u128> {
-        let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
-        self.try_to_bits(size)
-    }
-
-    pub fn from_bool(b: bool) -> Self {
-        ConstValue::Scalar(Scalar::from_bool(b))
-    }
-
-    pub fn from_u64(i: u64) -> Self {
-        ConstValue::Scalar(Scalar::from_u64(i))
-    }
-
-    pub fn from_u128(i: u128) -> Self {
-        ConstValue::Scalar(Scalar::from_u128(i))
-    }
-
-    pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
-        ConstValue::Scalar(Scalar::from_target_usize(i, cx))
-    }
-
-    /// Must only be called on constants of type `&str` or `&[u8]`!
-    pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
-        let (data, start, end) = match self {
-            ConstValue::Scalar(_) | ConstValue::ZeroSized => {
-                bug!("`try_get_slice_bytes` on non-slice constant")
-            }
-            &ConstValue::Slice { data, start, end } => (data, start, end),
-            &ConstValue::Indirect { alloc_id, offset } => {
-                // The reference itself is stored behind an indirection.
-                // Load the reference, and then load the actual slice contents.
-                let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
-                let ptr_size = tcx.data_layout.pointer_size;
-                if a.size() < offset + 2 * ptr_size {
-                    // (partially) dangling reference
-                    return None;
-                }
-                // Read the wide pointer components.
-                let ptr = a
-                    .read_scalar(
-                        &tcx,
-                        alloc_range(offset, ptr_size),
-                        /* read_provenance */ true,
-                    )
-                    .ok()?;
-                let ptr = ptr.to_pointer(&tcx).ok()?;
-                let len = a
-                    .read_scalar(
-                        &tcx,
-                        alloc_range(offset + ptr_size, ptr_size),
-                        /* read_provenance */ false,
-                    )
-                    .ok()?;
-                let len = len.to_target_usize(&tcx).ok()?;
-                let len: usize = len.try_into().ok()?;
-                if len == 0 {
-                    return Some(&[]);
-                }
-                // Non-empty slice, must have memory. We know this is a relative pointer.
-                let (inner_alloc_id, offset) = ptr.into_parts();
-                let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
-                (data, offset.bytes_usize(), offset.bytes_usize() + len)
-            }
-        };
-
-        // This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
-        Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
-    }
-}
+use super::{AllocId, InterpResult, Pointer, PointerArithmetic, Provenance, ScalarSizeMismatch};
 
 /// A `Scalar` represents an immediate, primitive value existing outside of a
 /// `memory::Allocation`. It is in many ways like a small chunk of an `Allocation`, up to 16 bytes in
@@ -328,6 +174,16 @@ impl<Prov> Scalar<Prov> {
     }
 
     #[inline]
+    pub fn from_i8(i: i8) -> Self {
+        Self::from_int(i, Size::from_bits(8))
+    }
+
+    #[inline]
+    pub fn from_i16(i: i16) -> Self {
+        Self::from_int(i, Size::from_bits(16))
+    }
+
+    #[inline]
     pub fn from_i32(i: i32) -> Self {
         Self::from_int(i, Size::from_bits(32))
     }
@@ -555,14 +411,18 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
     }
 
     #[inline]
+    pub fn to_float<F: Float>(self) -> InterpResult<'tcx, F> {
+        // Going through `to_uint` to check size and truncation.
+        Ok(F::from_bits(self.to_uint(Size::from_bits(F::BITS))?))
+    }
+
+    #[inline]
     pub fn to_f32(self) -> InterpResult<'tcx, Single> {
-        // Going through `u32` to check size and truncation.
-        Ok(Single::from_bits(self.to_u32()?.into()))
+        self.to_float()
     }
 
     #[inline]
     pub fn to_f64(self) -> InterpResult<'tcx, Double> {
-        // Going through `u64` to check size and truncation.
-        Ok(Double::from_bits(self.to_u64()?.into()))
+        self.to_float()
     }
 }
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index b8032733868..87180b56baa 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -2,27 +2,25 @@
 //!
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
 
-use crate::mir::interpret::{
-    AllocRange, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
-};
+use crate::mir::interpret::{AllocRange, ConstAllocation, ErrorHandled, Scalar};
 use crate::mir::visit::MirVisitable;
 use crate::ty::codec::{TyDecoder, TyEncoder};
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
-use crate::ty::print::with_no_trimmed_paths;
+use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
 use crate::ty::print::{FmtPrinter, Printer};
 use crate::ty::visit::TypeVisitableExt;
 use crate::ty::{self, List, Ty, TyCtxt};
-use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
-use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
+use crate::ty::{AdtDef, InstanceDef, UserTypeAnnotationIndex};
+use crate::ty::{GenericArg, GenericArgsRef};
 
 use rustc_data_structures::captures::Captures;
 use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_hir::def::{CtorKind, Namespace};
-use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_hir::{self, GeneratorKind, ImplicitSelfKind};
 use rustc_hir::{self as hir, HirId};
 use rustc_session::Session;
-use rustc_target::abi::{FieldIdx, Size, VariantIdx};
+use rustc_target::abi::{FieldIdx, VariantIdx};
 
 use polonius_engine::Atom;
 pub use rustc_ast::Mutability;
@@ -39,7 +37,7 @@ use either::Either;
 use std::borrow::Cow;
 use std::cell::RefCell;
 use std::collections::hash_map::Entry;
-use std::fmt::{self, Debug, Display, Formatter, Write};
+use std::fmt::{self, Debug, Formatter};
 use std::ops::{Index, IndexMut};
 use std::{iter, mem};
 
@@ -47,6 +45,7 @@ pub use self::query::*;
 pub use basic_blocks::BasicBlocks;
 
 mod basic_blocks;
+mod consts;
 pub mod coverage;
 mod generic_graph;
 pub mod generic_graphviz;
@@ -57,11 +56,10 @@ pub mod patch;
 pub mod pretty;
 mod query;
 pub mod spanview;
+mod statement;
 mod syntax;
-pub use syntax::*;
 pub mod tcx;
-pub mod terminator;
-pub use terminator::*;
+mod terminator;
 
 pub mod traversal;
 mod type_foldable;
@@ -72,6 +70,11 @@ pub use self::graphviz::write_mir_graphviz;
 pub use self::pretty::{
     create_dump_file, display_allocation, dump_enabled, dump_mir, write_mir_pretty, PassWhere,
 };
+pub use consts::*;
+use pretty::pretty_print_const_value;
+pub use statement::*;
+pub use syntax::*;
+pub use terminator::*;
 
 /// Types for locals
 pub type LocalDecls<'tcx> = IndexSlice<Local, LocalDecl<'tcx>>;
@@ -1149,20 +1152,6 @@ pub struct VarDebugInfo<'tcx> {
     pub argument_index: Option<u16>,
 }
 
-impl Debug for VarDebugInfo<'_> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
-            pre_fmt_projection(&projection[..], fmt)?;
-            write!(fmt, "({}: {})", self.name, ty)?;
-            post_fmt_projection(&projection[..], fmt)?;
-        } else {
-            write!(fmt, "{}", self.name)?;
-        }
-
-        write!(fmt, " => {:?}", self.value)
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlock
 
@@ -1319,576 +1308,6 @@ impl<'tcx> BasicBlockData<'tcx> {
     }
 }
 
-impl<O> AssertKind<O> {
-    /// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
-    pub fn is_optional_overflow_check(&self) -> bool {
-        use AssertKind::*;
-        use BinOp::*;
-        matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
-    }
-
-    /// Get the message that is printed at runtime when this assertion fails.
-    ///
-    /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
-    /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
-    /// instead of printing a static message.
-    pub fn description(&self) -> &'static str {
-        use AssertKind::*;
-        match self {
-            Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
-            Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
-            Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
-            Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
-            Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
-            OverflowNeg(_) => "attempt to negate with overflow",
-            Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
-            Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
-            Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
-            DivisionByZero(_) => "attempt to divide by zero",
-            RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
-            ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
-            ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
-            ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
-            ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
-            BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
-                bug!("Unexpected AssertKind")
-            }
-        }
-    }
-
-    /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
-    ///
-    /// Needs to be kept in sync with the run-time behavior (which is defined by
-    /// `AssertKind::description` and the lang items mentioned in its docs).
-    /// Note that we deliberately show more details here than we do at runtime, such as the actual
-    /// numbers that overflowed -- it is much easier to do so here than at runtime.
-    pub fn fmt_assert_args<W: Write>(&self, f: &mut W) -> fmt::Result
-    where
-        O: Debug,
-    {
-        use AssertKind::*;
-        match self {
-            BoundsCheck { ref len, ref index } => write!(
-                f,
-                "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
-            ),
-
-            OverflowNeg(op) => {
-                write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
-            }
-            DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
-            RemainderByZero(op) => write!(
-                f,
-                "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
-            ),
-            Overflow(BinOp::Add, l, r) => write!(
-                f,
-                "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
-            ),
-            Overflow(BinOp::Sub, l, r) => write!(
-                f,
-                "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
-            ),
-            Overflow(BinOp::Mul, l, r) => write!(
-                f,
-                "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
-            ),
-            Overflow(BinOp::Div, l, r) => write!(
-                f,
-                "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
-            ),
-            Overflow(BinOp::Rem, l, r) => write!(
-                f,
-                "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
-            ),
-            Overflow(BinOp::Shr, _, r) => {
-                write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
-            }
-            Overflow(BinOp::Shl, _, r) => {
-                write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
-            }
-            MisalignedPointerDereference { required, found } => {
-                write!(
-                    f,
-                    "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
-                )
-            }
-            _ => write!(f, "\"{}\"", self.description()),
-        }
-    }
-
-    /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
-    ///
-    /// Needs to be kept in sync with the run-time behavior (which is defined by
-    /// `AssertKind::description` and the lang items mentioned in its docs).
-    /// Note that we deliberately show more details here than we do at runtime, such as the actual
-    /// numbers that overflowed -- it is much easier to do so here than at runtime.
-    pub fn diagnostic_message(&self) -> DiagnosticMessage {
-        use crate::fluent_generated::*;
-        use AssertKind::*;
-
-        match self {
-            BoundsCheck { .. } => middle_bounds_check,
-            Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
-            Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
-            Overflow(_, _, _) => middle_assert_op_overflow,
-            OverflowNeg(_) => middle_assert_overflow_neg,
-            DivisionByZero(_) => middle_assert_divide_by_zero,
-            RemainderByZero(_) => middle_assert_remainder_by_zero,
-            ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
-            ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
-            ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
-            ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
-
-            MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
-        }
-    }
-
-    pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
-    where
-        O: fmt::Debug,
-    {
-        use AssertKind::*;
-
-        macro_rules! add {
-            ($name: expr, $value: expr) => {
-                adder($name.into(), $value.into_diagnostic_arg());
-            };
-        }
-
-        match self {
-            BoundsCheck { len, index } => {
-                add!("len", format!("{len:?}"));
-                add!("index", format!("{index:?}"));
-            }
-            Overflow(BinOp::Shl | BinOp::Shr, _, val)
-            | DivisionByZero(val)
-            | RemainderByZero(val)
-            | OverflowNeg(val) => {
-                add!("val", format!("{val:#?}"));
-            }
-            Overflow(binop, left, right) => {
-                add!("op", binop.to_hir_binop().as_str());
-                add!("left", format!("{left:#?}"));
-                add!("right", format!("{right:#?}"));
-            }
-            ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
-            MisalignedPointerDereference { required, found } => {
-                add!("required", format!("{required:#?}"));
-                add!("found", format!("{found:#?}"));
-            }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Statements
-
-/// A statement in a basic block, including information about its source code.
-#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
-pub struct Statement<'tcx> {
-    pub source_info: SourceInfo,
-    pub kind: StatementKind<'tcx>,
-}
-
-impl Statement<'_> {
-    /// Changes a statement to a nop. This is both faster than deleting instructions and avoids
-    /// invalidating statement indices in `Location`s.
-    pub fn make_nop(&mut self) {
-        self.kind = StatementKind::Nop
-    }
-
-    /// Changes a statement to a nop and returns the original statement.
-    #[must_use = "If you don't need the statement, use `make_nop` instead"]
-    pub fn replace_nop(&mut self) -> Self {
-        Statement {
-            source_info: self.source_info,
-            kind: mem::replace(&mut self.kind, StatementKind::Nop),
-        }
-    }
-}
-
-impl Debug for Statement<'_> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        use self::StatementKind::*;
-        match self.kind {
-            Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
-            FakeRead(box (ref cause, ref place)) => {
-                write!(fmt, "FakeRead({cause:?}, {place:?})")
-            }
-            Retag(ref kind, ref place) => write!(
-                fmt,
-                "Retag({}{:?})",
-                match kind {
-                    RetagKind::FnEntry => "[fn entry] ",
-                    RetagKind::TwoPhase => "[2phase] ",
-                    RetagKind::Raw => "[raw] ",
-                    RetagKind::Default => "",
-                },
-                place,
-            ),
-            StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
-            StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
-            SetDiscriminant { ref place, variant_index } => {
-                write!(fmt, "discriminant({place:?}) = {variant_index:?}")
-            }
-            Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
-            PlaceMention(ref place) => {
-                write!(fmt, "PlaceMention({place:?})")
-            }
-            AscribeUserType(box (ref place, ref c_ty), ref variance) => {
-                write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
-            }
-            Coverage(box self::Coverage { ref kind, code_region: Some(ref rgn) }) => {
-                write!(fmt, "Coverage::{kind:?} for {rgn:?}")
-            }
-            Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
-            Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
-            ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
-            Nop => write!(fmt, "nop"),
-        }
-    }
-}
-
-impl<'tcx> StatementKind<'tcx> {
-    pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
-        match self {
-            StatementKind::Assign(x) => Some(x),
-            _ => None,
-        }
-    }
-
-    pub fn as_assign(&self) -> Option<&(Place<'tcx>, Rvalue<'tcx>)> {
-        match self {
-            StatementKind::Assign(x) => Some(x),
-            _ => None,
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Places
-
-impl<V, T> ProjectionElem<V, T> {
-    /// Returns `true` if the target of this projection may refer to a different region of memory
-    /// than the base.
-    fn is_indirect(&self) -> bool {
-        match self {
-            Self::Deref => true,
-
-            Self::Field(_, _)
-            | Self::Index(_)
-            | Self::OpaqueCast(_)
-            | Self::ConstantIndex { .. }
-            | Self::Subslice { .. }
-            | Self::Downcast(_, _) => false,
-        }
-    }
-
-    /// Returns `true` if the target of this projection always refers to the same memory region
-    /// whatever the state of the program.
-    pub fn is_stable_offset(&self) -> bool {
-        match self {
-            Self::Deref | Self::Index(_) => false,
-            Self::Field(_, _)
-            | Self::OpaqueCast(_)
-            | Self::ConstantIndex { .. }
-            | Self::Subslice { .. }
-            | Self::Downcast(_, _) => true,
-        }
-    }
-
-    /// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
-    pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
-        matches!(*self, Self::Downcast(_, x) if x == v)
-    }
-
-    /// Returns `true` if this is a `Field` projection with the given index.
-    pub fn is_field_to(&self, f: FieldIdx) -> bool {
-        matches!(*self, Self::Field(x, _) if x == f)
-    }
-
-    /// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
-    pub fn can_use_in_debuginfo(&self) -> bool {
-        match self {
-            Self::ConstantIndex { from_end: false, .. }
-            | Self::Deref
-            | Self::Downcast(_, _)
-            | Self::Field(_, _) => true,
-            Self::ConstantIndex { from_end: true, .. }
-            | Self::Index(_)
-            | Self::OpaqueCast(_)
-            | Self::Subslice { .. } => false,
-        }
-    }
-}
-
-/// Alias for projections as they appear in `UserTypeProjection`, where we
-/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
-pub type ProjectionKind = ProjectionElem<(), ()>;
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct PlaceRef<'tcx> {
-    pub local: Local,
-    pub projection: &'tcx [PlaceElem<'tcx>],
-}
-
-// Once we stop implementing `Ord` for `DefId`,
-// this impl will be unnecessary. Until then, we'll
-// leave this impl in place to prevent re-adding a
-// dependency on the `Ord` impl for `DefId`
-impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
-
-impl<'tcx> Place<'tcx> {
-    // FIXME change this to a const fn by also making List::empty a const fn.
-    pub fn return_place() -> Place<'tcx> {
-        Place { local: RETURN_PLACE, projection: List::empty() }
-    }
-
-    /// Returns `true` if this `Place` contains a `Deref` projection.
-    ///
-    /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
-    /// same region of memory as its base.
-    pub fn is_indirect(&self) -> bool {
-        self.projection.iter().any(|elem| elem.is_indirect())
-    }
-
-    /// Returns `true` if this `Place`'s first projection is `Deref`.
-    ///
-    /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
-    /// `Deref` projections can only occur as the first projection. In that case this method
-    /// is equivalent to `is_indirect`, but faster.
-    pub fn is_indirect_first_projection(&self) -> bool {
-        self.as_ref().is_indirect_first_projection()
-    }
-
-    /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
-    /// a single deref of a local.
-    #[inline(always)]
-    pub fn local_or_deref_local(&self) -> Option<Local> {
-        self.as_ref().local_or_deref_local()
-    }
-
-    /// If this place represents a local variable like `_X` with no
-    /// projections, return `Some(_X)`.
-    #[inline(always)]
-    pub fn as_local(&self) -> Option<Local> {
-        self.as_ref().as_local()
-    }
-
-    #[inline]
-    pub fn as_ref(&self) -> PlaceRef<'tcx> {
-        PlaceRef { local: self.local, projection: &self.projection }
-    }
-
-    /// Iterate over the projections in evaluation order, i.e., the first element is the base with
-    /// its projection and then subsequently more projections are added.
-    /// As a concrete example, given the place a.b.c, this would yield:
-    /// - (a, .b)
-    /// - (a.b, .c)
-    ///
-    /// Given a place without projections, the iterator is empty.
-    #[inline]
-    pub fn iter_projections(
-        self,
-    ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
-        self.as_ref().iter_projections()
-    }
-
-    /// Generates a new place by appending `more_projections` to the existing ones
-    /// and interning the result.
-    pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
-        if more_projections.is_empty() {
-            return self;
-        }
-
-        self.as_ref().project_deeper(more_projections, tcx)
-    }
-}
-
-impl From<Local> for Place<'_> {
-    #[inline]
-    fn from(local: Local) -> Self {
-        Place { local, projection: List::empty() }
-    }
-}
-
-impl<'tcx> PlaceRef<'tcx> {
-    /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
-    /// a single deref of a local.
-    pub fn local_or_deref_local(&self) -> Option<Local> {
-        match *self {
-            PlaceRef { local, projection: [] }
-            | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
-            _ => None,
-        }
-    }
-
-    /// Returns `true` if this `Place` contains a `Deref` projection.
-    ///
-    /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
-    /// same region of memory as its base.
-    pub fn is_indirect(&self) -> bool {
-        self.projection.iter().any(|elem| elem.is_indirect())
-    }
-
-    /// Returns `true` if this `Place`'s first projection is `Deref`.
-    ///
-    /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
-    /// `Deref` projections can only occur as the first projection. In that case this method
-    /// is equivalent to `is_indirect`, but faster.
-    pub fn is_indirect_first_projection(&self) -> bool {
-        // To make sure this is not accidentally used in wrong mir phase
-        debug_assert!(
-            self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
-        );
-        self.projection.first() == Some(&PlaceElem::Deref)
-    }
-
-    /// If this place represents a local variable like `_X` with no
-    /// projections, return `Some(_X)`.
-    #[inline]
-    pub fn as_local(&self) -> Option<Local> {
-        match *self {
-            PlaceRef { local, projection: [] } => Some(local),
-            _ => None,
-        }
-    }
-
-    #[inline]
-    pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
-        if let &[ref proj_base @ .., elem] = self.projection {
-            Some((PlaceRef { local: self.local, projection: proj_base }, elem))
-        } else {
-            None
-        }
-    }
-
-    /// Iterate over the projections in evaluation order, i.e., the first element is the base with
-    /// its projection and then subsequently more projections are added.
-    /// As a concrete example, given the place a.b.c, this would yield:
-    /// - (a, .b)
-    /// - (a.b, .c)
-    ///
-    /// Given a place without projections, the iterator is empty.
-    #[inline]
-    pub fn iter_projections(
-        self,
-    ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
-        self.projection.iter().enumerate().map(move |(i, proj)| {
-            let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
-            (base, *proj)
-        })
-    }
-
-    /// Generates a new place by appending `more_projections` to the existing ones
-    /// and interning the result.
-    pub fn project_deeper(
-        self,
-        more_projections: &[PlaceElem<'tcx>],
-        tcx: TyCtxt<'tcx>,
-    ) -> Place<'tcx> {
-        let mut v: Vec<PlaceElem<'tcx>>;
-
-        let new_projections = if self.projection.is_empty() {
-            more_projections
-        } else {
-            v = Vec::with_capacity(self.projection.len() + more_projections.len());
-            v.extend(self.projection);
-            v.extend(more_projections);
-            &v
-        };
-
-        Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
-    }
-}
-
-impl From<Local> for PlaceRef<'_> {
-    #[inline]
-    fn from(local: Local) -> Self {
-        PlaceRef { local, projection: &[] }
-    }
-}
-
-impl Debug for Place<'_> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        self.as_ref().fmt(fmt)
-    }
-}
-
-impl Debug for PlaceRef<'_> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        pre_fmt_projection(self.projection, fmt)?;
-        write!(fmt, "{:?}", self.local)?;
-        post_fmt_projection(self.projection, fmt)
-    }
-}
-
-fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
-    for &elem in projection.iter().rev() {
-        match elem {
-            ProjectionElem::OpaqueCast(_)
-            | ProjectionElem::Downcast(_, _)
-            | ProjectionElem::Field(_, _) => {
-                write!(fmt, "(").unwrap();
-            }
-            ProjectionElem::Deref => {
-                write!(fmt, "(*").unwrap();
-            }
-            ProjectionElem::Index(_)
-            | ProjectionElem::ConstantIndex { .. }
-            | ProjectionElem::Subslice { .. } => {}
-        }
-    }
-
-    Ok(())
-}
-
-fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
-    for &elem in projection.iter() {
-        match elem {
-            ProjectionElem::OpaqueCast(ty) => {
-                write!(fmt, " as {ty})")?;
-            }
-            ProjectionElem::Downcast(Some(name), _index) => {
-                write!(fmt, " as {name})")?;
-            }
-            ProjectionElem::Downcast(None, index) => {
-                write!(fmt, " as variant#{index:?})")?;
-            }
-            ProjectionElem::Deref => {
-                write!(fmt, ")")?;
-            }
-            ProjectionElem::Field(field, ty) => {
-                with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
-            }
-            ProjectionElem::Index(ref index) => {
-                write!(fmt, "[{index:?}]")?;
-            }
-            ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
-                write!(fmt, "[{offset:?} of {min_length:?}]")?;
-            }
-            ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
-                write!(fmt, "[-{offset:?} of {min_length:?}]")?;
-            }
-            ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
-                write!(fmt, "[{from:?}:]")?;
-            }
-            ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
-                write!(fmt, "[:-{to:?}]")?;
-            }
-            ProjectionElem::Subslice { from, to, from_end: true } => {
-                write!(fmt, "[{from:?}:-{to:?}]")?;
-            }
-            ProjectionElem::Subslice { from, to, from_end: false } => {
-                write!(fmt, "[{from:?}..{to:?}]")?;
-            }
-        }
-    }
-
-    Ok(())
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // Scopes
 
@@ -1967,716 +1386,12 @@ pub struct SourceScopeLocalData {
     pub safety: Safety,
 }
 
-///////////////////////////////////////////////////////////////////////////
-// Operands
-
-impl<'tcx> Debug for Operand<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        use self::Operand::*;
-        match *self {
-            Constant(ref a) => write!(fmt, "{a:?}"),
-            Copy(ref place) => write!(fmt, "{place:?}"),
-            Move(ref place) => write!(fmt, "move {place:?}"),
-        }
-    }
-}
-
-impl<'tcx> Operand<'tcx> {
-    /// Convenience helper to make a constant that refers to the fn
-    /// with given `DefId` and args. Since this is used to synthesize
-    /// MIR, assumes `user_ty` is None.
-    pub fn function_handle(
-        tcx: TyCtxt<'tcx>,
-        def_id: DefId,
-        args: impl IntoIterator<Item = GenericArg<'tcx>>,
-        span: Span,
-    ) -> Self {
-        let ty = Ty::new_fn_def(tcx, def_id, args);
-        Operand::Constant(Box::new(Constant {
-            span,
-            user_ty: None,
-            literal: ConstantKind::Val(ConstValue::ZeroSized, ty),
-        }))
-    }
-
-    pub fn is_move(&self) -> bool {
-        matches!(self, Operand::Move(..))
-    }
-
-    /// Convenience helper to make a literal-like constant from a given scalar value.
-    /// Since this is used to synthesize MIR, assumes `user_ty` is None.
-    pub fn const_from_scalar(
-        tcx: TyCtxt<'tcx>,
-        ty: Ty<'tcx>,
-        val: Scalar,
-        span: Span,
-    ) -> Operand<'tcx> {
-        debug_assert!({
-            let param_env_and_ty = ty::ParamEnv::empty().and(ty);
-            let type_size = tcx
-                .layout_of(param_env_and_ty)
-                .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}"))
-                .size;
-            let scalar_size = match val {
-                Scalar::Int(int) => int.size(),
-                _ => panic!("Invalid scalar type {val:?}"),
-            };
-            scalar_size == type_size
-        });
-        Operand::Constant(Box::new(Constant {
-            span,
-            user_ty: None,
-            literal: ConstantKind::Val(ConstValue::Scalar(val), ty),
-        }))
-    }
-
-    pub fn to_copy(&self) -> Self {
-        match *self {
-            Operand::Copy(_) | Operand::Constant(_) => self.clone(),
-            Operand::Move(place) => Operand::Copy(place),
-        }
-    }
-
-    /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a
-    /// constant.
-    pub fn place(&self) -> Option<Place<'tcx>> {
-        match self {
-            Operand::Copy(place) | Operand::Move(place) => Some(*place),
-            Operand::Constant(_) => None,
-        }
-    }
-
-    /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
-    /// place.
-    pub fn constant(&self) -> Option<&Constant<'tcx>> {
-        match self {
-            Operand::Constant(x) => Some(&**x),
-            Operand::Copy(_) | Operand::Move(_) => None,
-        }
-    }
-
-    /// Gets the `ty::FnDef` from an operand if it's a constant function item.
-    ///
-    /// While this is unlikely in general, it's the normal case of what you'll
-    /// find as the `func` in a [`TerminatorKind::Call`].
-    pub fn const_fn_def(&self) -> Option<(DefId, GenericArgsRef<'tcx>)> {
-        let const_ty = self.constant()?.literal.ty();
-        if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-/// Rvalues
-
-impl<'tcx> Rvalue<'tcx> {
-    /// Returns true if rvalue can be safely removed when the result is unused.
-    #[inline]
-    pub fn is_safe_to_remove(&self) -> bool {
-        match self {
-            // Pointer to int casts may be side-effects due to exposing the provenance.
-            // While the model is undecided, we should be conservative. See
-            // <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
-            Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
-
-            Rvalue::Use(_)
-            | Rvalue::CopyForDeref(_)
-            | Rvalue::Repeat(_, _)
-            | Rvalue::Ref(_, _, _)
-            | Rvalue::ThreadLocalRef(_)
-            | Rvalue::AddressOf(_, _)
-            | Rvalue::Len(_)
-            | Rvalue::Cast(
-                CastKind::IntToInt
-                | CastKind::FloatToInt
-                | CastKind::FloatToFloat
-                | CastKind::IntToFloat
-                | CastKind::FnPtrToPtr
-                | CastKind::PtrToPtr
-                | CastKind::PointerCoercion(_)
-                | CastKind::PointerFromExposedAddress
-                | CastKind::DynStar
-                | CastKind::Transmute,
-                _,
-                _,
-            )
-            | Rvalue::BinaryOp(_, _)
-            | Rvalue::CheckedBinaryOp(_, _)
-            | Rvalue::NullaryOp(_, _)
-            | Rvalue::UnaryOp(_, _)
-            | Rvalue::Discriminant(_)
-            | Rvalue::Aggregate(_, _)
-            | Rvalue::ShallowInitBox(_, _) => true,
-        }
-    }
-}
-
-impl BorrowKind {
-    pub fn mutability(&self) -> Mutability {
-        match *self {
-            BorrowKind::Shared | BorrowKind::Shallow => Mutability::Not,
-            BorrowKind::Mut { .. } => Mutability::Mut,
-        }
-    }
-
-    pub fn allows_two_phase_borrow(&self) -> bool {
-        match *self {
-            BorrowKind::Shared
-            | BorrowKind::Shallow
-            | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
-                false
-            }
-            BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
-        }
-    }
-}
-
-impl<'tcx> Debug for Rvalue<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        use self::Rvalue::*;
-
-        match *self {
-            Use(ref place) => write!(fmt, "{place:?}"),
-            Repeat(ref a, b) => {
-                write!(fmt, "[{a:?}; ")?;
-                pretty_print_const(b, fmt, false)?;
-                write!(fmt, "]")
-            }
-            Len(ref a) => write!(fmt, "Len({a:?})"),
-            Cast(ref kind, ref place, ref ty) => {
-                with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
-            }
-            BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
-            CheckedBinaryOp(ref op, box (ref a, ref b)) => {
-                write!(fmt, "Checked{op:?}({a:?}, {b:?})")
-            }
-            UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
-            Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
-            NullaryOp(ref op, ref t) => {
-                let t = with_no_trimmed_paths!(format!("{}", t));
-                match op {
-                    NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
-                    NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
-                    NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
-                }
-            }
-            ThreadLocalRef(did) => ty::tls::with(|tcx| {
-                let muta = tcx.static_mutability(did).unwrap().prefix_str();
-                write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
-            }),
-            Ref(region, borrow_kind, ref place) => {
-                let kind_str = match borrow_kind {
-                    BorrowKind::Shared => "",
-                    BorrowKind::Shallow => "shallow ",
-                    BorrowKind::Mut { .. } => "mut ",
-                };
-
-                // When printing regions, add trailing space if necessary.
-                let print_region = ty::tls::with(|tcx| {
-                    tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions
-                });
-                let region = if print_region {
-                    let mut region = region.to_string();
-                    if !region.is_empty() {
-                        region.push(' ');
-                    }
-                    region
-                } else {
-                    // Do not even print 'static
-                    String::new()
-                };
-                write!(fmt, "&{region}{kind_str}{place:?}")
-            }
-
-            CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
-
-            AddressOf(mutability, ref place) => {
-                let kind_str = match mutability {
-                    Mutability::Mut => "mut",
-                    Mutability::Not => "const",
-                };
-
-                write!(fmt, "&raw {kind_str} {place:?}")
-            }
-
-            Aggregate(ref kind, ref places) => {
-                let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
-                    let mut tuple_fmt = fmt.debug_tuple(name);
-                    for place in places {
-                        tuple_fmt.field(place);
-                    }
-                    tuple_fmt.finish()
-                };
-
-                match **kind {
-                    AggregateKind::Array(_) => write!(fmt, "{places:?}"),
-
-                    AggregateKind::Tuple => {
-                        if places.is_empty() {
-                            write!(fmt, "()")
-                        } else {
-                            fmt_tuple(fmt, "")
-                        }
-                    }
-
-                    AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
-                        ty::tls::with(|tcx| {
-                            let variant_def = &tcx.adt_def(adt_did).variant(variant);
-                            let args = tcx.lift(args).expect("could not lift for printing");
-                            let name = FmtPrinter::new(tcx, Namespace::ValueNS)
-                                .print_def_path(variant_def.def_id, args)?
-                                .into_buffer();
-
-                            match variant_def.ctor_kind() {
-                                Some(CtorKind::Const) => fmt.write_str(&name),
-                                Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
-                                None => {
-                                    let mut struct_fmt = fmt.debug_struct(&name);
-                                    for (field, place) in iter::zip(&variant_def.fields, places) {
-                                        struct_fmt.field(field.name.as_str(), place);
-                                    }
-                                    struct_fmt.finish()
-                                }
-                            }
-                        })
-                    }
-
-                    AggregateKind::Closure(def_id, args) => ty::tls::with(|tcx| {
-                        let name = if tcx.sess.opts.unstable_opts.span_free_formats {
-                            let args = tcx.lift(args).unwrap();
-                            format!("[closure@{}]", tcx.def_path_str_with_args(def_id, args),)
-                        } else {
-                            let span = tcx.def_span(def_id);
-                            format!(
-                                "[closure@{}]",
-                                tcx.sess.source_map().span_to_diagnostic_string(span)
-                            )
-                        };
-                        let mut struct_fmt = fmt.debug_struct(&name);
-
-                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places
-                        if let Some(def_id) = def_id.as_local()
-                            && let Some(upvars) = tcx.upvars_mentioned(def_id)
-                        {
-                            for (&var_id, place) in iter::zip(upvars.keys(), places) {
-                                let var_name = tcx.hir().name(var_id);
-                                struct_fmt.field(var_name.as_str(), place);
-                            }
-                        } else {
-                            for (index, place) in places.iter().enumerate() {
-                                struct_fmt.field(&format!("{index}"), place);
-                            }
-                        }
-
-                        struct_fmt.finish()
-                    }),
-
-                    AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
-                        let name = format!("[generator@{:?}]", tcx.def_span(def_id));
-                        let mut struct_fmt = fmt.debug_struct(&name);
-
-                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places
-                        if let Some(def_id) = def_id.as_local()
-                            && let Some(upvars) = tcx.upvars_mentioned(def_id)
-                        {
-                            for (&var_id, place) in iter::zip(upvars.keys(), places) {
-                                let var_name = tcx.hir().name(var_id);
-                                struct_fmt.field(var_name.as_str(), place);
-                            }
-                        } else {
-                            for (index, place) in places.iter().enumerate() {
-                                struct_fmt.field(&format!("{index}"), place);
-                            }
-                        }
-
-                        struct_fmt.finish()
-                    }),
-                }
-            }
-
-            ShallowInitBox(ref place, ref ty) => {
-                with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
-            }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-/// Constants
-///
-/// Two constants are equal if they are the same constant. Note that
-/// this does not necessarily mean that they are `==` in Rust. In
-/// particular, one must be wary of `NaN`!
-
-#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct Constant<'tcx> {
-    pub span: Span,
-
-    /// Optional user-given type: for something like
-    /// `collect::<Vec<_>>`, this would be present and would
-    /// indicate that `Vec<_>` was explicitly specified.
-    ///
-    /// Needed for NLL to impose user-given type constraints.
-    pub user_ty: Option<UserTypeAnnotationIndex>,
-
-    pub literal: ConstantKind<'tcx>,
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub enum ConstantKind<'tcx> {
-    /// This constant came from the type system.
-    ///
-    /// Any way of turning `ty::Const` into `ConstValue` should go through `valtree_to_const_val`;
-    /// this ensures that we consistently produce "clean" values without data in the padding or
-    /// anything like that.
-    Ty(ty::Const<'tcx>),
-
-    /// An unevaluated mir constant which is not part of the type system.
-    Unevaluated(UnevaluatedConst<'tcx>, Ty<'tcx>),
-
-    /// This constant cannot go back into the type system, as it represents
-    /// something the type system cannot handle (e.g. pointers).
-    Val(interpret::ConstValue<'tcx>, Ty<'tcx>),
-}
-
-impl<'tcx> Constant<'tcx> {
-    pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
-        match self.literal.try_to_scalar() {
-            Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
-                GlobalAlloc::Static(def_id) => {
-                    assert!(!tcx.is_thread_local_static(def_id));
-                    Some(def_id)
-                }
-                _ => None,
-            },
-            _ => None,
-        }
-    }
-    #[inline]
-    pub fn ty(&self) -> Ty<'tcx> {
-        self.literal.ty()
-    }
-}
-
-impl<'tcx> ConstantKind<'tcx> {
-    #[inline(always)]
-    pub fn ty(&self) -> Ty<'tcx> {
-        match self {
-            ConstantKind::Ty(c) => c.ty(),
-            ConstantKind::Val(_, ty) | ConstantKind::Unevaluated(_, ty) => *ty,
-        }
-    }
-
-    #[inline]
-    pub fn try_to_scalar(self) -> Option<Scalar> {
-        match self {
-            ConstantKind::Ty(c) => match c.kind() {
-                ty::ConstKind::Value(valtree) => match valtree {
-                    ty::ValTree::Leaf(scalar_int) => Some(Scalar::Int(scalar_int)),
-                    ty::ValTree::Branch(_) => None,
-                },
-                _ => None,
-            },
-            ConstantKind::Val(val, _) => val.try_to_scalar(),
-            ConstantKind::Unevaluated(..) => None,
-        }
-    }
-
-    #[inline]
-    pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
-        self.try_to_scalar()?.try_to_int().ok()
-    }
-
-    #[inline]
-    pub fn try_to_bits(self, size: Size) -> Option<u128> {
-        self.try_to_scalar_int()?.to_bits(size).ok()
-    }
-
-    #[inline]
-    pub fn try_to_bool(self) -> Option<bool> {
-        self.try_to_scalar_int()?.try_into().ok()
-    }
-
-    #[inline]
-    pub fn eval(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        span: Option<Span>,
-    ) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> {
-        match self {
-            ConstantKind::Ty(c) => {
-                // We want to consistently have a "clean" value for type system constants (i.e., no
-                // data hidden in the padding), so we always go through a valtree here.
-                let val = c.eval(tcx, param_env, span)?;
-                Ok(tcx.valtree_to_const_val((self.ty(), val)))
-            }
-            ConstantKind::Unevaluated(uneval, _) => {
-                // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`
-                tcx.const_eval_resolve(param_env, uneval, span)
-            }
-            ConstantKind::Val(val, _) => Ok(val),
-        }
-    }
-
-    /// Normalizes the constant to a value or an error if possible.
-    #[inline]
-    pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
-        match self.eval(tcx, param_env, None) {
-            Ok(val) => Self::Val(val, self.ty()),
-            Err(ErrorHandled::Reported(guar, _span)) => {
-                Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
-            }
-            Err(ErrorHandled::TooGeneric(_span)) => self,
-        }
-    }
-
-    #[inline]
-    pub fn try_eval_scalar(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-    ) -> Option<Scalar> {
-        self.eval(tcx, param_env, None).ok()?.try_to_scalar()
-    }
-
-    #[inline]
-    pub fn try_eval_scalar_int(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-    ) -> Option<ScalarInt> {
-        self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
-    }
-
-    #[inline]
-    pub fn try_eval_bits(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Option<u128> {
-        let int = self.try_eval_scalar_int(tcx, param_env)?;
-        assert_eq!(self.ty(), ty);
-        let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
-        int.to_bits(size).ok()
-    }
-
-    /// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
-    #[inline]
-    pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
-        self.try_eval_bits(tcx, param_env, ty)
-            .unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
-    }
-
-    #[inline]
-    pub fn try_eval_target_usize(
-        self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-    ) -> Option<u64> {
-        self.try_eval_scalar_int(tcx, param_env)?.try_to_target_usize(tcx).ok()
-    }
-
-    #[inline]
-    /// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
-    pub fn eval_target_usize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u64 {
-        self.try_eval_target_usize(tcx, param_env)
-            .unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
-    }
-
-    #[inline]
-    pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Option<bool> {
-        self.try_eval_scalar_int(tcx, param_env)?.try_into().ok()
-    }
-
-    #[inline]
-    pub fn from_value(val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
-        Self::Val(val, ty)
-    }
-
-    pub fn from_bits(
-        tcx: TyCtxt<'tcx>,
-        bits: u128,
-        param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
-    ) -> Self {
-        let size = tcx
-            .layout_of(param_env_ty)
-            .unwrap_or_else(|e| {
-                bug!("could not compute layout for {:?}: {:?}", param_env_ty.value, e)
-            })
-            .size;
-        let cv = ConstValue::Scalar(Scalar::from_uint(bits, size));
-
-        Self::Val(cv, param_env_ty.value)
-    }
-
-    #[inline]
-    pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
-        let cv = ConstValue::from_bool(v);
-        Self::Val(cv, tcx.types.bool)
-    }
-
-    #[inline]
-    pub fn zero_sized(ty: Ty<'tcx>) -> Self {
-        let cv = ConstValue::ZeroSized;
-        Self::Val(cv, ty)
-    }
-
-    pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
-        let ty = tcx.types.usize;
-        Self::from_bits(tcx, n as u128, ty::ParamEnv::empty().and(ty))
-    }
-
-    #[inline]
-    pub fn from_scalar(_tcx: TyCtxt<'tcx>, s: Scalar, ty: Ty<'tcx>) -> Self {
-        let val = ConstValue::Scalar(s);
-        Self::Val(val, ty)
-    }
-
-    /// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly
-    /// converted to a constant, everything else becomes `Unevaluated`.
-    #[instrument(skip(tcx), level = "debug", ret)]
-    pub fn from_anon_const(
-        tcx: TyCtxt<'tcx>,
-        def: LocalDefId,
-        param_env: ty::ParamEnv<'tcx>,
-    ) -> Self {
-        let body_id = match tcx.hir().get_by_def_id(def) {
-            hir::Node::AnonConst(ac) => ac.body,
-            _ => {
-                span_bug!(tcx.def_span(def), "from_anon_const can only process anonymous constants")
-            }
-        };
-
-        let expr = &tcx.hir().body(body_id).value;
-        debug!(?expr);
-
-        // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
-        // currently have to be wrapped in curly brackets, so it's necessary to special-case.
-        let expr = match &expr.kind {
-            hir::ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_some() => {
-                block.expr.as_ref().unwrap()
-            }
-            _ => expr,
-        };
-        debug!("expr.kind: {:?}", expr.kind);
-
-        let ty = tcx.type_of(def).instantiate_identity();
-        debug!(?ty);
-
-        // FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
-        // does not provide the parents generics to anonymous constants. We still allow generic const
-        // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
-        // ever try to substitute the generic parameters in their bodies.
-        //
-        // While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
-        // cause issues if we were to remove that special-case and try to evaluate the constant instead.
-        use hir::{def::DefKind::ConstParam, def::Res, ExprKind, Path, QPath};
-        match expr.kind {
-            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 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.item_name(def_id);
-                let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
-                debug!(?ty_const);
-
-                return Self::Ty(ty_const);
-            }
-            _ => {}
-        }
-
-        let hir_id = tcx.hir().local_def_id_to_hir_id(def);
-        let parent_args = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id)
-            && let Some(parent_did) = parent_hir_id.as_owner()
-        {
-            GenericArgs::identity_for_item(tcx, parent_did)
-        } else {
-            List::empty()
-        };
-        debug!(?parent_args);
-
-        let did = def.to_def_id();
-        let child_args = GenericArgs::identity_for_item(tcx, did);
-        let args = tcx.mk_args_from_iter(parent_args.into_iter().chain(child_args.into_iter()));
-        debug!(?args);
-
-        let span = tcx.def_span(def);
-        let uneval = UnevaluatedConst::new(did, args);
-        debug!(?span, ?param_env);
-
-        match tcx.const_eval_resolve(param_env, uneval, Some(span)) {
-            Ok(val) => {
-                debug!("evaluated const value");
-                Self::Val(val, ty)
-            }
-            Err(_) => {
-                debug!("error encountered during evaluation");
-                // Error was handled in `const_eval_resolve`. Here we just create a
-                // new unevaluated const and error hard later in codegen
-                Self::Unevaluated(
-                    UnevaluatedConst {
-                        def: did,
-                        args: GenericArgs::identity_for_item(tcx, did),
-                        promoted: None,
-                    },
-                    ty,
-                )
-            }
-        }
-    }
-
-    pub fn from_ty_const(c: ty::Const<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
-        match c.kind() {
-            ty::ConstKind::Value(valtree) => {
-                // Make sure that if `c` is normalized, then the return value is normalized.
-                let const_val = tcx.valtree_to_const_val((c.ty(), valtree));
-                Self::Val(const_val, c.ty())
-            }
-            _ => Self::Ty(c),
-        }
-    }
-}
-
-/// An unevaluated (potentially generic) constant used in MIR.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
-#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
-pub struct UnevaluatedConst<'tcx> {
-    pub def: DefId,
-    pub args: GenericArgsRef<'tcx>,
-    pub promoted: Option<Promoted>,
-}
-
-impl<'tcx> UnevaluatedConst<'tcx> {
-    #[inline]
-    pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
-        assert_eq!(self.promoted, None);
-        ty::UnevaluatedConst { def: self.def, args: self.args }
-    }
-}
-
-impl<'tcx> UnevaluatedConst<'tcx> {
-    #[inline]
-    pub fn new(def: DefId, args: GenericArgsRef<'tcx>) -> UnevaluatedConst<'tcx> {
-        UnevaluatedConst { def, args, promoted: Default::default() }
-    }
-
-    #[inline]
-    pub fn from_instance(instance: ty::Instance<'tcx>) -> Self {
-        UnevaluatedConst::new(instance.def_id(), instance.args)
-    }
-}
-
 /// A collection of projections into user types.
 ///
 /// They are projections because a binding can occur a part of a
 /// parent pattern that has been ascribed a type.
 ///
-/// Its a collection because there can be multiple type ascriptions on
+/// It's a collection because there can be multiple type ascriptions on
 /// the path from the root of the pattern down to the binding itself.
 ///
 /// An example:
@@ -2830,206 +1545,6 @@ rustc_index::newtype_index! {
     pub struct Promoted {}
 }
 
-impl<'tcx> Debug for Constant<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        write!(fmt, "{self}")
-    }
-}
-
-impl<'tcx> Display for Constant<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        match self.ty().kind() {
-            ty::FnDef(..) => {}
-            _ => write!(fmt, "const ")?,
-        }
-        Display::fmt(&self.literal, fmt)
-    }
-}
-
-impl<'tcx> Display for ConstantKind<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        match *self {
-            ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
-            ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
-            // FIXME(valtrees): Correctly print mir constants.
-            ConstantKind::Unevaluated(..) => {
-                fmt.write_str("_")?;
-                Ok(())
-            }
-        }
-    }
-}
-
-fn pretty_print_const<'tcx>(
-    c: ty::Const<'tcx>,
-    fmt: &mut Formatter<'_>,
-    print_types: bool,
-) -> fmt::Result {
-    use crate::ty::print::PrettyPrinter;
-    ty::tls::with(|tcx| {
-        let literal = tcx.lift(c).unwrap();
-        let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
-        cx.print_alloc_ids = true;
-        let cx = cx.pretty_print_const(literal, print_types)?;
-        fmt.write_str(&cx.into_buffer())?;
-        Ok(())
-    })
-}
-
-fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
-    write!(fmt, "b\"{}\"", byte_str.escape_ascii())
-}
-
-fn comma_sep<'tcx>(
-    fmt: &mut Formatter<'_>,
-    elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
-) -> fmt::Result {
-    let mut first = true;
-    for (ct, ty) in elems {
-        if !first {
-            fmt.write_str(", ")?;
-        }
-        pretty_print_const_value(ct, ty, fmt)?;
-        first = false;
-    }
-    Ok(())
-}
-
-// FIXME: Move that into `mir/pretty.rs`.
-fn pretty_print_const_value<'tcx>(
-    ct: ConstValue<'tcx>,
-    ty: Ty<'tcx>,
-    fmt: &mut Formatter<'_>,
-) -> fmt::Result {
-    use crate::ty::print::PrettyPrinter;
-
-    ty::tls::with(|tcx| {
-        let ct = tcx.lift(ct).unwrap();
-        let ty = tcx.lift(ty).unwrap();
-
-        if tcx.sess.verbose() {
-            fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
-            return Ok(());
-        }
-
-        let u8_type = tcx.types.u8;
-        match (ct, ty.kind()) {
-            // Byte/string slices, printed as (byte) string literals.
-            (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
-                if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
-                    fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
-                    return Ok(());
-                }
-            }
-            (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
-                if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
-                    pretty_print_byte_str(fmt, data)?;
-                    return Ok(());
-                }
-            }
-            (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
-                let n = n.try_to_target_usize(tcx).unwrap();
-                let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
-                // cast is ok because we already checked for pointer size (32 or 64 bit) above
-                let range = AllocRange { start: offset, size: Size::from_bytes(n) };
-                let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
-                fmt.write_str("*")?;
-                pretty_print_byte_str(fmt, byte_str)?;
-                return Ok(());
-            }
-            // Aggregates, printed as array/tuple/struct/variant construction syntax.
-            //
-            // NB: the `has_non_region_param` check ensures that we can use
-            // the `destructure_const` query with an empty `ty::ParamEnv` without
-            // introducing ICEs (e.g. via `layout_of`) from missing bounds.
-            // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
-            // to be able to destructure the tuple into `(0u8, *mut T)`
-            (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
-                let ct = tcx.lift(ct).unwrap();
-                let ty = tcx.lift(ty).unwrap();
-                if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
-                    let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
-                    match *ty.kind() {
-                        ty::Array(..) => {
-                            fmt.write_str("[")?;
-                            comma_sep(fmt, fields)?;
-                            fmt.write_str("]")?;
-                        }
-                        ty::Tuple(..) => {
-                            fmt.write_str("(")?;
-                            comma_sep(fmt, fields)?;
-                            if contents.fields.len() == 1 {
-                                fmt.write_str(",")?;
-                            }
-                            fmt.write_str(")")?;
-                        }
-                        ty::Adt(def, _) if def.variants().is_empty() => {
-                            fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
-                        }
-                        ty::Adt(def, args) => {
-                            let variant_idx = contents
-                                .variant
-                                .expect("destructed mir constant of adt without variant idx");
-                            let variant_def = &def.variant(variant_idx);
-                            let args = tcx.lift(args).unwrap();
-                            let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
-                            cx.print_alloc_ids = true;
-                            let cx = cx.print_value_path(variant_def.def_id, args)?;
-                            fmt.write_str(&cx.into_buffer())?;
-
-                            match variant_def.ctor_kind() {
-                                Some(CtorKind::Const) => {}
-                                Some(CtorKind::Fn) => {
-                                    fmt.write_str("(")?;
-                                    comma_sep(fmt, fields)?;
-                                    fmt.write_str(")")?;
-                                }
-                                None => {
-                                    fmt.write_str(" {{ ")?;
-                                    let mut first = true;
-                                    for (field_def, (ct, ty)) in
-                                        iter::zip(&variant_def.fields, fields)
-                                    {
-                                        if !first {
-                                            fmt.write_str(", ")?;
-                                        }
-                                        write!(fmt, "{}: ", field_def.name)?;
-                                        pretty_print_const_value(ct, ty, fmt)?;
-                                        first = false;
-                                    }
-                                    fmt.write_str(" }}")?;
-                                }
-                            }
-                        }
-                        _ => unreachable!(),
-                    }
-                    return Ok(());
-                }
-            }
-            (ConstValue::Scalar(scalar), _) => {
-                let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
-                cx.print_alloc_ids = true;
-                let ty = tcx.lift(ty).unwrap();
-                cx = cx.pretty_print_const_scalar(scalar, ty)?;
-                fmt.write_str(&cx.into_buffer())?;
-                return Ok(());
-            }
-            (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
-                let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
-                cx.print_alloc_ids = true;
-                let cx = cx.print_value_path(*d, s)?;
-                fmt.write_str(&cx.into_buffer())?;
-                return Ok(());
-            }
-            // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
-            // their fields instead of just dumping the memory.
-            _ => {}
-        }
-        // Fall back to debug pretty printing for invalid constants.
-        write!(fmt, "{ct:?}: {ty}")
-    })
-}
-
 /// `Location` represents the position of the start of the statement; or, if
 /// `statement_index` equals the number of statements, then the start of the
 /// terminator.
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index c4fae27a7a5..632f159a7a8 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1,19 +1,19 @@
 use std::collections::BTreeSet;
-use std::fmt::Display;
-use std::fmt::Write as _;
+use std::fmt::{self, Debug, Display, Write as _};
 use std::fs;
-use std::io::{self, Write};
+use std::io::{self, Write as _};
 use std::path::{Path, PathBuf};
 
 use super::graphviz::write_mir_fn_graphviz;
 use super::spanview::write_mir_fn_spanview;
 use either::Either;
+use rustc_ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
 use rustc_index::Idx;
 use rustc_middle::mir::interpret::{
-    alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, ConstValue,
-    GlobalAlloc, Pointer, Provenance,
+    alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc,
+    Pointer, Provenance,
 };
 use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
@@ -79,7 +79,7 @@ pub fn dump_mir<'tcx, F>(
     body: &Body<'tcx>,
     extra_data: F,
 ) where
-    F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
 {
     if !dump_enabled(tcx, pass_name, body.source.def_id()) {
         return;
@@ -116,7 +116,7 @@ fn dump_matched_mir_node<'tcx, F>(
     body: &Body<'tcx>,
     mut extra_data: F,
 ) where
-    F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
 {
     let _: io::Result<()> = try {
         let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
@@ -260,11 +260,14 @@ pub fn create_dump_file<'tcx>(
     )
 }
 
+///////////////////////////////////////////////////////////////////////////
+// Whole MIR bodies
+
 /// Write out a human-readable textual representation for the given MIR.
 pub fn write_mir_pretty<'tcx>(
     tcx: TyCtxt<'tcx>,
     single: Option<DefId>,
-    w: &mut dyn Write,
+    w: &mut dyn io::Write,
 ) -> io::Result<()> {
     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
@@ -278,7 +281,7 @@ pub fn write_mir_pretty<'tcx>(
             writeln!(w)?;
         }
 
-        let render_body = |w: &mut dyn Write, body| -> io::Result<()> {
+        let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
             write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
 
             for body in tcx.promoted_mir(def_id) {
@@ -309,10 +312,10 @@ pub fn write_mir_fn<'tcx, F>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
     extra_data: &mut F,
-    w: &mut dyn Write,
+    w: &mut dyn io::Write,
 ) -> io::Result<()>
 where
-    F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
 {
     write_mir_intro(tcx, body, w)?;
     for block in body.basic_blocks.indices() {
@@ -330,16 +333,267 @@ where
     Ok(())
 }
 
+/// Prints local variables in a scope tree.
+fn write_scope_tree(
+    tcx: TyCtxt<'_>,
+    body: &Body<'_>,
+    scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
+    w: &mut dyn io::Write,
+    parent: SourceScope,
+    depth: usize,
+) -> io::Result<()> {
+    let indent = depth * INDENT.len();
+
+    // Local variable debuginfo.
+    for var_debug_info in &body.var_debug_info {
+        if var_debug_info.source_info.scope != parent {
+            // Not declared in this scope.
+            continue;
+        }
+
+        let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
+
+        if tcx.sess.opts.unstable_opts.mir_include_spans {
+            writeln!(
+                w,
+                "{0:1$} // in {2}",
+                indented_debug_info,
+                ALIGN,
+                comment(tcx, var_debug_info.source_info),
+            )?;
+        } else {
+            writeln!(w, "{indented_debug_info}")?;
+        }
+    }
+
+    // Local variable types.
+    for (local, local_decl) in body.local_decls.iter_enumerated() {
+        if (1..body.arg_count + 1).contains(&local.index()) {
+            // Skip over argument locals, they're printed in the signature.
+            continue;
+        }
+
+        if local_decl.source_info.scope != parent {
+            // Not declared in this scope.
+            continue;
+        }
+
+        let mut_str = local_decl.mutability.prefix_str();
+
+        let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
+            "{0:1$}let {2}{3:?}: {4}",
+            INDENT, indent, mut_str, local, local_decl.ty
+        ));
+        if let Some(user_ty) = &local_decl.user_ty {
+            for user_ty in user_ty.projections() {
+                write!(indented_decl, " as {user_ty:?}").unwrap();
+            }
+        }
+        indented_decl.push(';');
+
+        let local_name = if local == RETURN_PLACE { " return place" } else { "" };
+
+        if tcx.sess.opts.unstable_opts.mir_include_spans {
+            writeln!(
+                w,
+                "{0:1$} //{2} in {3}",
+                indented_decl,
+                ALIGN,
+                local_name,
+                comment(tcx, local_decl.source_info),
+            )?;
+        } else {
+            writeln!(w, "{indented_decl}",)?;
+        }
+    }
+
+    let Some(children) = scope_tree.get(&parent) else {
+        return Ok(());
+    };
+
+    for &child in children {
+        let child_data = &body.source_scopes[child];
+        assert_eq!(child_data.parent_scope, Some(parent));
+
+        let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
+            (
+                format!(
+                    " (inlined {}{})",
+                    if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
+                    callee
+                ),
+                Some(callsite_span),
+            )
+        } else {
+            (String::new(), None)
+        };
+
+        let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
+
+        if tcx.sess.opts.unstable_opts.mir_include_spans {
+            if let Some(span) = span {
+                writeln!(
+                    w,
+                    "{0:1$} // at {2}",
+                    indented_header,
+                    ALIGN,
+                    tcx.sess.source_map().span_to_embeddable_string(span),
+                )?;
+            } else {
+                writeln!(w, "{indented_header}")?;
+            }
+        } else {
+            writeln!(w, "{indented_header}")?;
+        }
+
+        write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
+        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
+    }
+
+    Ok(())
+}
+
+impl Debug for VarDebugInfo<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
+            pre_fmt_projection(&projection[..], fmt)?;
+            write!(fmt, "({}: {})", self.name, ty)?;
+            post_fmt_projection(&projection[..], fmt)?;
+        } else {
+            write!(fmt, "{}", self.name)?;
+        }
+
+        write!(fmt, " => {:?}", self.value)
+    }
+}
+
+/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
+/// local variables (both user-defined bindings and compiler temporaries).
+pub fn write_mir_intro<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'_>,
+    w: &mut dyn io::Write,
+) -> io::Result<()> {
+    write_mir_sig(tcx, body, w)?;
+    writeln!(w, "{{")?;
+
+    // construct a scope tree and write it out
+    let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
+    for (index, scope_data) in body.source_scopes.iter().enumerate() {
+        if let Some(parent) = scope_data.parent_scope {
+            scope_tree.entry(parent).or_default().push(SourceScope::new(index));
+        } else {
+            // Only the argument scope has no parent, because it's the root.
+            assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
+        }
+    }
+
+    write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
+
+    // Add an empty line before the first block is printed.
+    writeln!(w)?;
+
+    Ok(())
+}
+
+fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io::Result<()> {
+    use rustc_hir::def::DefKind;
+
+    trace!("write_mir_sig: {:?}", body.source.instance);
+    let def_id = body.source.def_id();
+    let kind = tcx.def_kind(def_id);
+    let is_function = match kind {
+        DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
+        _ => tcx.is_closure(def_id),
+    };
+    match (kind, body.source.promoted) {
+        (_, Some(i)) => write!(w, "{i:?} in ")?,
+        (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
+        (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
+        (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
+        (_, _) if is_function => write!(w, "fn ")?,
+        (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
+        _ => bug!("Unexpected def kind {:?}", kind),
+    }
+
+    ty::print::with_forced_impl_filename_line! {
+        // see notes on #41697 elsewhere
+        write!(w, "{}", tcx.def_path_str(def_id))?
+    }
+
+    if body.source.promoted.is_none() && is_function {
+        write!(w, "(")?;
+
+        // fn argument types.
+        for (i, arg) in body.args_iter().enumerate() {
+            if i != 0 {
+                write!(w, ", ")?;
+            }
+            write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
+        }
+
+        write!(w, ") -> {}", body.return_ty())?;
+    } else {
+        assert_eq!(body.arg_count, 0);
+        write!(w, ": {} =", body.return_ty())?;
+    }
+
+    if let Some(yield_ty) = body.yield_ty() {
+        writeln!(w)?;
+        writeln!(w, "yields {yield_ty}")?;
+    }
+
+    write!(w, " ")?;
+    // Next thing that gets printed is the opening {
+
+    Ok(())
+}
+
+fn write_user_type_annotations(
+    tcx: TyCtxt<'_>,
+    body: &Body<'_>,
+    w: &mut dyn io::Write,
+) -> io::Result<()> {
+    if !body.user_type_annotations.is_empty() {
+        writeln!(w, "| User Type Annotations")?;
+    }
+    for (index, annotation) in body.user_type_annotations.iter_enumerated() {
+        writeln!(
+            w,
+            "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
+            index.index(),
+            annotation.user_ty,
+            tcx.sess.source_map().span_to_embeddable_string(annotation.span),
+            with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
+        )?;
+    }
+    if !body.user_type_annotations.is_empty() {
+        writeln!(w, "|")?;
+    }
+    Ok(())
+}
+
+pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
+    if let Some(i) = single {
+        vec![i]
+    } else {
+        tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Basic blocks and their parts (statements, terminators, ...)
+
 /// Write out a human-readable textual representation for the given basic block.
 pub fn write_basic_block<'tcx, F>(
     tcx: TyCtxt<'tcx>,
     block: BasicBlock,
     body: &Body<'tcx>,
     extra_data: &mut F,
-    w: &mut dyn Write,
+    w: &mut dyn io::Write,
 ) -> io::Result<()>
 where
-    F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
+    F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
 {
     let data = &body[block];
 
@@ -400,10 +654,497 @@ where
     writeln!(w, "{INDENT}}}")
 }
 
+impl Debug for Statement<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        use self::StatementKind::*;
+        match self.kind {
+            Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"),
+            FakeRead(box (ref cause, ref place)) => {
+                write!(fmt, "FakeRead({cause:?}, {place:?})")
+            }
+            Retag(ref kind, ref place) => write!(
+                fmt,
+                "Retag({}{:?})",
+                match kind {
+                    RetagKind::FnEntry => "[fn entry] ",
+                    RetagKind::TwoPhase => "[2phase] ",
+                    RetagKind::Raw => "[raw] ",
+                    RetagKind::Default => "",
+                },
+                place,
+            ),
+            StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"),
+            StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"),
+            SetDiscriminant { ref place, variant_index } => {
+                write!(fmt, "discriminant({place:?}) = {variant_index:?}")
+            }
+            Deinit(ref place) => write!(fmt, "Deinit({place:?})"),
+            PlaceMention(ref place) => {
+                write!(fmt, "PlaceMention({place:?})")
+            }
+            AscribeUserType(box (ref place, ref c_ty), ref variance) => {
+                write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
+            }
+            Coverage(box self::Coverage { ref kind, code_region: Some(ref rgn) }) => {
+                write!(fmt, "Coverage::{kind:?} for {rgn:?}")
+            }
+            Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
+            Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
+            ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
+            Nop => write!(fmt, "nop"),
+        }
+    }
+}
+
+impl<'tcx> Debug for TerminatorKind<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        self.fmt_head(fmt)?;
+        let successor_count = self.successors().count();
+        let labels = self.fmt_successor_labels();
+        assert_eq!(successor_count, labels.len());
+
+        // `Cleanup` is already included in successors
+        let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
+        let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
+            write!(fmt, "unwind ")?;
+            match self.unwind() {
+                // Not needed or included in successors
+                None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
+                Some(UnwindAction::Continue) => write!(fmt, "continue"),
+                Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
+                Some(UnwindAction::Terminate(reason)) => {
+                    write!(fmt, "terminate({})", reason.as_short_str())
+                }
+            }
+        };
+
+        match (successor_count, show_unwind) {
+            (0, false) => Ok(()),
+            (0, true) => {
+                write!(fmt, " -> ")?;
+                fmt_unwind(fmt)
+            }
+            (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
+            _ => {
+                write!(fmt, " -> [")?;
+                for (i, target) in self.successors().enumerate() {
+                    if i > 0 {
+                        write!(fmt, ", ")?;
+                    }
+                    write!(fmt, "{}: {:?}", labels[i], target)?;
+                }
+                if show_unwind {
+                    write!(fmt, ", ")?;
+                    fmt_unwind(fmt)?;
+                }
+                write!(fmt, "]")
+            }
+        }
+    }
+}
+
+impl<'tcx> TerminatorKind<'tcx> {
+    /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
+    /// successor basic block, if any. The only information not included is the list of possible
+    /// successors, which may be rendered differently between the text and the graphviz format.
+    pub fn fmt_head<W: fmt::Write>(&self, fmt: &mut W) -> fmt::Result {
+        use self::TerminatorKind::*;
+        match self {
+            Goto { .. } => write!(fmt, "goto"),
+            SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
+            Return => write!(fmt, "return"),
+            GeneratorDrop => write!(fmt, "generator_drop"),
+            UnwindResume => write!(fmt, "resume"),
+            UnwindTerminate(reason) => {
+                write!(fmt, "abort({})", reason.as_short_str())
+            }
+            Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
+            Unreachable => write!(fmt, "unreachable"),
+            Drop { place, .. } => write!(fmt, "drop({place:?})"),
+            Call { func, args, destination, .. } => {
+                write!(fmt, "{destination:?} = ")?;
+                write!(fmt, "{func:?}(")?;
+                for (index, arg) in args.iter().enumerate() {
+                    if index > 0 {
+                        write!(fmt, ", ")?;
+                    }
+                    write!(fmt, "{arg:?}")?;
+                }
+                write!(fmt, ")")
+            }
+            Assert { cond, expected, msg, .. } => {
+                write!(fmt, "assert(")?;
+                if !expected {
+                    write!(fmt, "!")?;
+                }
+                write!(fmt, "{cond:?}, ")?;
+                msg.fmt_assert_args(fmt)?;
+                write!(fmt, ")")
+            }
+            FalseEdge { .. } => write!(fmt, "falseEdge"),
+            FalseUnwind { .. } => write!(fmt, "falseUnwind"),
+            InlineAsm { template, ref operands, options, .. } => {
+                write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
+                for op in operands {
+                    write!(fmt, ", ")?;
+                    let print_late = |&late| if late { "late" } else { "" };
+                    match op {
+                        InlineAsmOperand::In { reg, value } => {
+                            write!(fmt, "in({reg}) {value:?}")?;
+                        }
+                        InlineAsmOperand::Out { reg, late, place: Some(place) } => {
+                            write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
+                        }
+                        InlineAsmOperand::Out { reg, late, place: None } => {
+                            write!(fmt, "{}out({}) _", print_late(late), reg)?;
+                        }
+                        InlineAsmOperand::InOut {
+                            reg,
+                            late,
+                            in_value,
+                            out_place: Some(out_place),
+                        } => {
+                            write!(
+                                fmt,
+                                "in{}out({}) {:?} => {:?}",
+                                print_late(late),
+                                reg,
+                                in_value,
+                                out_place
+                            )?;
+                        }
+                        InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
+                            write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
+                        }
+                        InlineAsmOperand::Const { value } => {
+                            write!(fmt, "const {value:?}")?;
+                        }
+                        InlineAsmOperand::SymFn { value } => {
+                            write!(fmt, "sym_fn {value:?}")?;
+                        }
+                        InlineAsmOperand::SymStatic { def_id } => {
+                            write!(fmt, "sym_static {def_id:?}")?;
+                        }
+                    }
+                }
+                write!(fmt, ", options({options:?}))")
+            }
+        }
+    }
+
+    /// Returns the list of labels for the edges to the successor basic blocks.
+    pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
+        use self::TerminatorKind::*;
+        match *self {
+            Return | UnwindResume | UnwindTerminate(_) | Unreachable | GeneratorDrop => vec![],
+            Goto { .. } => vec!["".into()],
+            SwitchInt { ref targets, .. } => targets
+                .values
+                .iter()
+                .map(|&u| Cow::Owned(u.to_string()))
+                .chain(iter::once("otherwise".into()))
+                .collect(),
+            Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
+                vec!["return".into(), "unwind".into()]
+            }
+            Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
+            Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
+            Call { target: None, unwind: _, .. } => vec![],
+            Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
+            Yield { drop: None, .. } => vec!["resume".into()],
+            Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
+            Drop { unwind: _, .. } => vec!["return".into()],
+            Assert { unwind: UnwindAction::Cleanup(_), .. } => {
+                vec!["success".into(), "unwind".into()]
+            }
+            Assert { unwind: _, .. } => vec!["success".into()],
+            FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
+            FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
+                vec!["real".into(), "unwind".into()]
+            }
+            FalseUnwind { unwind: _, .. } => vec!["real".into()],
+            InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
+                vec!["return".into(), "unwind".into()]
+            }
+            InlineAsm { destination: Some(_), unwind: _, .. } => {
+                vec!["return".into()]
+            }
+            InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => {
+                vec!["unwind".into()]
+            }
+            InlineAsm { destination: None, unwind: _, .. } => vec![],
+        }
+    }
+}
+
+impl<'tcx> Debug for Rvalue<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        use self::Rvalue::*;
+
+        match *self {
+            Use(ref place) => write!(fmt, "{place:?}"),
+            Repeat(ref a, b) => {
+                write!(fmt, "[{a:?}; ")?;
+                pretty_print_const(b, fmt, false)?;
+                write!(fmt, "]")
+            }
+            Len(ref a) => write!(fmt, "Len({a:?})"),
+            Cast(ref kind, ref place, ref ty) => {
+                with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})"))
+            }
+            BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"),
+            CheckedBinaryOp(ref op, box (ref a, ref b)) => {
+                write!(fmt, "Checked{op:?}({a:?}, {b:?})")
+            }
+            UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"),
+            Discriminant(ref place) => write!(fmt, "discriminant({place:?})"),
+            NullaryOp(ref op, ref t) => {
+                let t = with_no_trimmed_paths!(format!("{}", t));
+                match op {
+                    NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
+                    NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
+                    NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
+                }
+            }
+            ThreadLocalRef(did) => ty::tls::with(|tcx| {
+                let muta = tcx.static_mutability(did).unwrap().prefix_str();
+                write!(fmt, "&/*tls*/ {}{}", muta, tcx.def_path_str(did))
+            }),
+            Ref(region, borrow_kind, ref place) => {
+                let kind_str = match borrow_kind {
+                    BorrowKind::Shared => "",
+                    BorrowKind::Shallow => "shallow ",
+                    BorrowKind::Mut { .. } => "mut ",
+                };
+
+                // When printing regions, add trailing space if necessary.
+                let print_region = ty::tls::with(|tcx| {
+                    tcx.sess.verbose() || tcx.sess.opts.unstable_opts.identify_regions
+                });
+                let region = if print_region {
+                    let mut region = region.to_string();
+                    if !region.is_empty() {
+                        region.push(' ');
+                    }
+                    region
+                } else {
+                    // Do not even print 'static
+                    String::new()
+                };
+                write!(fmt, "&{region}{kind_str}{place:?}")
+            }
+
+            CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"),
+
+            AddressOf(mutability, ref place) => {
+                let kind_str = match mutability {
+                    Mutability::Mut => "mut",
+                    Mutability::Not => "const",
+                };
+
+                write!(fmt, "&raw {kind_str} {place:?}")
+            }
+
+            Aggregate(ref kind, ref places) => {
+                let fmt_tuple = |fmt: &mut Formatter<'_>, name: &str| {
+                    let mut tuple_fmt = fmt.debug_tuple(name);
+                    for place in places {
+                        tuple_fmt.field(place);
+                    }
+                    tuple_fmt.finish()
+                };
+
+                match **kind {
+                    AggregateKind::Array(_) => write!(fmt, "{places:?}"),
+
+                    AggregateKind::Tuple => {
+                        if places.is_empty() {
+                            write!(fmt, "()")
+                        } else {
+                            fmt_tuple(fmt, "")
+                        }
+                    }
+
+                    AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => {
+                        ty::tls::with(|tcx| {
+                            let variant_def = &tcx.adt_def(adt_did).variant(variant);
+                            let args = tcx.lift(args).expect("could not lift for printing");
+                            let name = FmtPrinter::new(tcx, Namespace::ValueNS)
+                                .print_def_path(variant_def.def_id, args)?
+                                .into_buffer();
+
+                            match variant_def.ctor_kind() {
+                                Some(CtorKind::Const) => fmt.write_str(&name),
+                                Some(CtorKind::Fn) => fmt_tuple(fmt, &name),
+                                None => {
+                                    let mut struct_fmt = fmt.debug_struct(&name);
+                                    for (field, place) in iter::zip(&variant_def.fields, places) {
+                                        struct_fmt.field(field.name.as_str(), place);
+                                    }
+                                    struct_fmt.finish()
+                                }
+                            }
+                        })
+                    }
+
+                    AggregateKind::Closure(def_id, args) => ty::tls::with(|tcx| {
+                        let name = if tcx.sess.opts.unstable_opts.span_free_formats {
+                            let args = tcx.lift(args).unwrap();
+                            format!("[closure@{}]", tcx.def_path_str_with_args(def_id, args),)
+                        } else {
+                            let span = tcx.def_span(def_id);
+                            format!(
+                                "[closure@{}]",
+                                tcx.sess.source_map().span_to_diagnostic_string(span)
+                            )
+                        };
+                        let mut struct_fmt = fmt.debug_struct(&name);
+
+                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places
+                        if let Some(def_id) = def_id.as_local()
+                            && let Some(upvars) = tcx.upvars_mentioned(def_id)
+                        {
+                            for (&var_id, place) in iter::zip(upvars.keys(), places) {
+                                let var_name = tcx.hir().name(var_id);
+                                struct_fmt.field(var_name.as_str(), place);
+                            }
+                        } else {
+                            for (index, place) in places.iter().enumerate() {
+                                struct_fmt.field(&format!("{index}"), place);
+                            }
+                        }
+
+                        struct_fmt.finish()
+                    }),
+
+                    AggregateKind::Generator(def_id, _, _) => ty::tls::with(|tcx| {
+                        let name = format!("[generator@{:?}]", tcx.def_span(def_id));
+                        let mut struct_fmt = fmt.debug_struct(&name);
+
+                        // FIXME(project-rfc-2229#48): This should be a list of capture names/places
+                        if let Some(def_id) = def_id.as_local()
+                            && let Some(upvars) = tcx.upvars_mentioned(def_id)
+                        {
+                            for (&var_id, place) in iter::zip(upvars.keys(), places) {
+                                let var_name = tcx.hir().name(var_id);
+                                struct_fmt.field(var_name.as_str(), place);
+                            }
+                        } else {
+                            for (index, place) in places.iter().enumerate() {
+                                struct_fmt.field(&format!("{index}"), place);
+                            }
+                        }
+
+                        struct_fmt.finish()
+                    }),
+                }
+            }
+
+            ShallowInitBox(ref place, ref ty) => {
+                with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
+            }
+        }
+    }
+}
+
+impl<'tcx> Debug for Operand<'tcx> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        use self::Operand::*;
+        match *self {
+            Constant(ref a) => write!(fmt, "{a:?}"),
+            Copy(ref place) => write!(fmt, "{place:?}"),
+            Move(ref place) => write!(fmt, "move {place:?}"),
+        }
+    }
+}
+
+impl Debug for Place<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        self.as_ref().fmt(fmt)
+    }
+}
+
+impl Debug for PlaceRef<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        pre_fmt_projection(self.projection, fmt)?;
+        write!(fmt, "{:?}", self.local)?;
+        post_fmt_projection(self.projection, fmt)
+    }
+}
+
+fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
+    for &elem in projection.iter().rev() {
+        match elem {
+            ProjectionElem::OpaqueCast(_)
+            | ProjectionElem::Downcast(_, _)
+            | ProjectionElem::Field(_, _) => {
+                write!(fmt, "(").unwrap();
+            }
+            ProjectionElem::Deref => {
+                write!(fmt, "(*").unwrap();
+            }
+            ProjectionElem::Index(_)
+            | ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subslice { .. } => {}
+        }
+    }
+
+    Ok(())
+}
+
+fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
+    for &elem in projection.iter() {
+        match elem {
+            ProjectionElem::OpaqueCast(ty) => {
+                write!(fmt, " as {ty})")?;
+            }
+            ProjectionElem::Downcast(Some(name), _index) => {
+                write!(fmt, " as {name})")?;
+            }
+            ProjectionElem::Downcast(None, index) => {
+                write!(fmt, " as variant#{index:?})")?;
+            }
+            ProjectionElem::Deref => {
+                write!(fmt, ")")?;
+            }
+            ProjectionElem::Field(field, ty) => {
+                with_no_trimmed_paths!(write!(fmt, ".{:?}: {})", field.index(), ty)?);
+            }
+            ProjectionElem::Index(ref index) => {
+                write!(fmt, "[{index:?}]")?;
+            }
+            ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => {
+                write!(fmt, "[{offset:?} of {min_length:?}]")?;
+            }
+            ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => {
+                write!(fmt, "[-{offset:?} of {min_length:?}]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => {
+                write!(fmt, "[{from:?}:]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => {
+                write!(fmt, "[:-{to:?}]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: true } => {
+                write!(fmt, "[{from:?}:-{to:?}]")?;
+            }
+            ProjectionElem::Subslice { from, to, from_end: false } => {
+                write!(fmt, "[{from:?}..{to:?}]")?;
+            }
+        }
+    }
+
+    Ok(())
+}
+
 /// After we print the main statement, we sometimes dump extra
 /// information. There's often a lot of little things "nuzzled up" in
 /// a statement.
-fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
+fn write_extra<'tcx, F>(
+    tcx: TyCtxt<'tcx>,
+    write: &mut dyn io::Write,
+    mut visit_op: F,
+) -> io::Result<()>
 where
     F: FnMut(&mut ExtraComments<'tcx>),
 {
@@ -455,26 +1196,27 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
                 self.push(&format!("+ user_ty: {user_ty:?}"));
             }
 
-            // FIXME: this is a poor version of `pretty_print_const_value`.
-            let fmt_val = |val: &ConstValue<'tcx>| match val {
-                ConstValue::ZeroSized => "<ZST>".to_string(),
-                ConstValue::Scalar(s) => format!("Scalar({s:?})"),
-                ConstValue::Slice { .. } => "Slice(..)".to_string(),
-                ConstValue::Indirect { .. } => "ByRef(..)".to_string(),
+            let fmt_val = |val: ConstValue<'tcx>, ty: Ty<'tcx>| {
+                let tcx = self.tcx;
+                rustc_data_structures::make_display(move |fmt| {
+                    pretty_print_const_value_tcx(tcx, val, ty, fmt)
+                })
             };
 
+            // FIXME: call pretty_print_const_valtree?
             let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
-                ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({leaf:?})"),
-                ty::ValTree::Branch(_) => "ValTree::Branch(..)".to_string(),
+                ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"),
+                ty::ValTree::Branch(_) => "Branch(..)".to_string(),
             };
 
             let val = match literal {
                 ConstantKind::Ty(ct) => match ct.kind() {
-                    ty::ConstKind::Param(p) => format!("Param({p})"),
+                    ty::ConstKind::Param(p) => format!("ty::Param({p})"),
                     ty::ConstKind::Unevaluated(uv) => {
-                        format!("Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
+                        format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
                     }
-                    ty::ConstKind::Value(val) => format!("Value({})", fmt_valtree(&val)),
+                    ty::ConstKind::Value(val) => format!("ty::Valtree({})", fmt_valtree(&val)),
+                    // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
                     ty::ConstKind::Error(_) => "Error".to_string(),
                     // These variants shouldn't exist in the MIR.
                     ty::ConstKind::Placeholder(_)
@@ -490,10 +1232,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
                         uv.promoted,
                     )
                 }
-                // To keep the diffs small, we render this like we render `ty::Const::Value`.
-                //
-                // This changes once `ty::Const::Value` is represented using valtrees.
-                ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)),
+                ConstantKind::Val(val, ty) => format!("Value({})", fmt_val(*val, *ty)),
             };
 
             // This reflects what `Const` looked liked before `val` was renamed
@@ -536,161 +1275,15 @@ fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
     format!("scope {} at {}", scope.index(), location,)
 }
 
-/// Prints local variables in a scope tree.
-fn write_scope_tree(
-    tcx: TyCtxt<'_>,
-    body: &Body<'_>,
-    scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
-    w: &mut dyn Write,
-    parent: SourceScope,
-    depth: usize,
-) -> io::Result<()> {
-    let indent = depth * INDENT.len();
-
-    // Local variable debuginfo.
-    for var_debug_info in &body.var_debug_info {
-        if var_debug_info.source_info.scope != parent {
-            // Not declared in this scope.
-            continue;
-        }
-
-        let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
-
-        if tcx.sess.opts.unstable_opts.mir_include_spans {
-            writeln!(
-                w,
-                "{0:1$} // in {2}",
-                indented_debug_info,
-                ALIGN,
-                comment(tcx, var_debug_info.source_info),
-            )?;
-        } else {
-            writeln!(w, "{indented_debug_info}")?;
-        }
-    }
-
-    // Local variable types.
-    for (local, local_decl) in body.local_decls.iter_enumerated() {
-        if (1..body.arg_count + 1).contains(&local.index()) {
-            // Skip over argument locals, they're printed in the signature.
-            continue;
-        }
-
-        if local_decl.source_info.scope != parent {
-            // Not declared in this scope.
-            continue;
-        }
-
-        let mut_str = local_decl.mutability.prefix_str();
-
-        let mut indented_decl = ty::print::with_no_trimmed_paths!(format!(
-            "{0:1$}let {2}{3:?}: {4}",
-            INDENT, indent, mut_str, local, local_decl.ty
-        ));
-        if let Some(user_ty) = &local_decl.user_ty {
-            for user_ty in user_ty.projections() {
-                write!(indented_decl, " as {user_ty:?}").unwrap();
-            }
-        }
-        indented_decl.push(';');
-
-        let local_name = if local == RETURN_PLACE { " return place" } else { "" };
-
-        if tcx.sess.opts.unstable_opts.mir_include_spans {
-            writeln!(
-                w,
-                "{0:1$} //{2} in {3}",
-                indented_decl,
-                ALIGN,
-                local_name,
-                comment(tcx, local_decl.source_info),
-            )?;
-        } else {
-            writeln!(w, "{indented_decl}",)?;
-        }
-    }
-
-    let Some(children) = scope_tree.get(&parent) else {
-        return Ok(());
-    };
-
-    for &child in children {
-        let child_data = &body.source_scopes[child];
-        assert_eq!(child_data.parent_scope, Some(parent));
-
-        let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
-            (
-                format!(
-                    " (inlined {}{})",
-                    if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
-                    callee
-                ),
-                Some(callsite_span),
-            )
-        } else {
-            (String::new(), None)
-        };
-
-        let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
-
-        if tcx.sess.opts.unstable_opts.mir_include_spans {
-            if let Some(span) = span {
-                writeln!(
-                    w,
-                    "{0:1$} // at {2}",
-                    indented_header,
-                    ALIGN,
-                    tcx.sess.source_map().span_to_embeddable_string(span),
-                )?;
-            } else {
-                writeln!(w, "{indented_header}")?;
-            }
-        } else {
-            writeln!(w, "{indented_header}")?;
-        }
-
-        write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
-        writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
-    }
-
-    Ok(())
-}
-
-/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
-/// local variables (both user-defined bindings and compiler temporaries).
-pub fn write_mir_intro<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body: &Body<'_>,
-    w: &mut dyn Write,
-) -> io::Result<()> {
-    write_mir_sig(tcx, body, w)?;
-    writeln!(w, "{{")?;
-
-    // construct a scope tree and write it out
-    let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
-    for (index, scope_data) in body.source_scopes.iter().enumerate() {
-        if let Some(parent) = scope_data.parent_scope {
-            scope_tree.entry(parent).or_default().push(SourceScope::new(index));
-        } else {
-            // Only the argument scope has no parent, because it's the root.
-            assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
-        }
-    }
-
-    write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
-
-    // Add an empty line before the first block is printed.
-    writeln!(w)?;
-
-    Ok(())
-}
+///////////////////////////////////////////////////////////////////////////
+// Allocations
 
 /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
 /// allocations.
 pub fn write_allocations<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'_>,
-    w: &mut dyn Write,
+    w: &mut dyn io::Write,
 ) -> io::Result<()> {
     fn alloc_ids_from_alloc(
         alloc: ConstAllocation<'_>,
@@ -739,7 +1332,7 @@ pub fn write_allocations<'tcx>(
     let mut todo: Vec<_> = seen.iter().copied().collect();
     while let Some(id) = todo.pop() {
         let mut write_allocation_track_relocs =
-            |w: &mut dyn Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
+            |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> {
                 // `.rev()` because we are popping them from the back of the `todo` vector.
                 for id in alloc_ids_from_alloc(alloc).rev() {
                     if seen.insert(id) {
@@ -1000,91 +1593,173 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
     Ok(())
 }
 
-fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> {
-    use rustc_hir::def::DefKind;
-
-    trace!("write_mir_sig: {:?}", body.source.instance);
-    let def_id = body.source.def_id();
-    let kind = tcx.def_kind(def_id);
-    let is_function = match kind {
-        DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
-        _ => tcx.is_closure(def_id),
-    };
-    match (kind, body.source.promoted) {
-        (_, Some(i)) => write!(w, "{i:?} in ")?,
-        (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
-        (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?,
-        (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?,
-        (_, _) if is_function => write!(w, "fn ")?,
-        (DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
-        _ => bug!("Unexpected def kind {:?}", kind),
-    }
-
-    ty::print::with_forced_impl_filename_line! {
-        // see notes on #41697 elsewhere
-        write!(w, "{}", tcx.def_path_str(def_id))?
-    }
+///////////////////////////////////////////////////////////////////////////
+// Constants
 
-    if body.source.promoted.is_none() && is_function {
-        write!(w, "(")?;
+fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Result {
+    write!(fmt, "b\"{}\"", byte_str.escape_ascii())
+}
 
-        // fn argument types.
-        for (i, arg) in body.args_iter().enumerate() {
-            if i != 0 {
-                write!(w, ", ")?;
-            }
-            write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
+fn comma_sep<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fmt: &mut Formatter<'_>,
+    elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
+) -> fmt::Result {
+    let mut first = true;
+    for (ct, ty) in elems {
+        if !first {
+            fmt.write_str(", ")?;
         }
-
-        write!(w, ") -> {}", body.return_ty())?;
-    } else {
-        assert_eq!(body.arg_count, 0);
-        write!(w, ": {} =", body.return_ty())?;
+        pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
+        first = false;
     }
-
-    if let Some(yield_ty) = body.yield_ty() {
-        writeln!(w)?;
-        writeln!(w, "yields {yield_ty}")?;
-    }
-
-    write!(w, " ")?;
-    // Next thing that gets printed is the opening {
-
     Ok(())
 }
 
-fn write_user_type_annotations(
-    tcx: TyCtxt<'_>,
-    body: &Body<'_>,
-    w: &mut dyn Write,
-) -> io::Result<()> {
-    if !body.user_type_annotations.is_empty() {
-        writeln!(w, "| User Type Annotations")?;
-    }
-    for (index, annotation) in body.user_type_annotations.iter_enumerated() {
-        writeln!(
-            w,
-            "| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
-            index.index(),
-            annotation.user_ty,
-            tcx.sess.source_map().span_to_embeddable_string(annotation.span),
-            with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
-        )?;
+fn pretty_print_const_value_tcx<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ct: ConstValue<'tcx>,
+    ty: Ty<'tcx>,
+    fmt: &mut Formatter<'_>,
+) -> fmt::Result {
+    use crate::ty::print::PrettyPrinter;
+
+    if tcx.sess.verbose() {
+        fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?;
+        return Ok(());
     }
-    if !body.user_type_annotations.is_empty() {
-        writeln!(w, "|")?;
+
+    let u8_type = tcx.types.u8;
+    match (ct, ty.kind()) {
+        // Byte/string slices, printed as (byte) string literals.
+        (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
+            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+                fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
+                return Ok(());
+            }
+        }
+        (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
+            if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
+                pretty_print_byte_str(fmt, data)?;
+                return Ok(());
+            }
+        }
+        (ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
+            let n = n.try_to_target_usize(tcx).unwrap();
+            let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
+            // cast is ok because we already checked for pointer size (32 or 64 bit) above
+            let range = AllocRange { start: offset, size: Size::from_bytes(n) };
+            let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
+            fmt.write_str("*")?;
+            pretty_print_byte_str(fmt, byte_str)?;
+            return Ok(());
+        }
+        // Aggregates, printed as array/tuple/struct/variant construction syntax.
+        //
+        // NB: the `has_non_region_param` check ensures that we can use
+        // the `destructure_const` query with an empty `ty::ParamEnv` without
+        // introducing ICEs (e.g. via `layout_of`) from missing bounds.
+        // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
+        // to be able to destructure the tuple into `(0u8, *mut T)`
+        (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
+            let ct = tcx.lift(ct).unwrap();
+            let ty = tcx.lift(ty).unwrap();
+            if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
+                let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
+                match *ty.kind() {
+                    ty::Array(..) => {
+                        fmt.write_str("[")?;
+                        comma_sep(tcx, fmt, fields)?;
+                        fmt.write_str("]")?;
+                    }
+                    ty::Tuple(..) => {
+                        fmt.write_str("(")?;
+                        comma_sep(tcx, fmt, fields)?;
+                        if contents.fields.len() == 1 {
+                            fmt.write_str(",")?;
+                        }
+                        fmt.write_str(")")?;
+                    }
+                    ty::Adt(def, _) if def.variants().is_empty() => {
+                        fmt.write_str(&format!("{{unreachable(): {ty}}}"))?;
+                    }
+                    ty::Adt(def, args) => {
+                        let variant_idx = contents
+                            .variant
+                            .expect("destructed mir constant of adt without variant idx");
+                        let variant_def = &def.variant(variant_idx);
+                        let args = tcx.lift(args).unwrap();
+                        let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+                        cx.print_alloc_ids = true;
+                        let cx = cx.print_value_path(variant_def.def_id, args)?;
+                        fmt.write_str(&cx.into_buffer())?;
+
+                        match variant_def.ctor_kind() {
+                            Some(CtorKind::Const) => {}
+                            Some(CtorKind::Fn) => {
+                                fmt.write_str("(")?;
+                                comma_sep(tcx, fmt, fields)?;
+                                fmt.write_str(")")?;
+                            }
+                            None => {
+                                fmt.write_str(" {{ ")?;
+                                let mut first = true;
+                                for (field_def, (ct, ty)) in iter::zip(&variant_def.fields, fields)
+                                {
+                                    if !first {
+                                        fmt.write_str(", ")?;
+                                    }
+                                    write!(fmt, "{}: ", field_def.name)?;
+                                    pretty_print_const_value_tcx(tcx, ct, ty, fmt)?;
+                                    first = false;
+                                }
+                                fmt.write_str(" }}")?;
+                            }
+                        }
+                    }
+                    _ => unreachable!(),
+                }
+                return Ok(());
+            }
+        }
+        (ConstValue::Scalar(scalar), _) => {
+            let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+            cx.print_alloc_ids = true;
+            let ty = tcx.lift(ty).unwrap();
+            cx = cx.pretty_print_const_scalar(scalar, ty)?;
+            fmt.write_str(&cx.into_buffer())?;
+            return Ok(());
+        }
+        (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
+            let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+            cx.print_alloc_ids = true;
+            let cx = cx.print_value_path(*d, s)?;
+            fmt.write_str(&cx.into_buffer())?;
+            return Ok(());
+        }
+        // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
+        // their fields instead of just dumping the memory.
+        _ => {}
     }
-    Ok(())
+    // Fall back to debug pretty printing for invalid constants.
+    write!(fmt, "{ct:?}: {ty}")
 }
 
-pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
-    if let Some(i) = single {
-        vec![i]
-    } else {
-        tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
-    }
+pub(crate) fn pretty_print_const_value<'tcx>(
+    ct: ConstValue<'tcx>,
+    ty: Ty<'tcx>,
+    fmt: &mut Formatter<'_>,
+) -> fmt::Result {
+    ty::tls::with(|tcx| {
+        let ct = tcx.lift(ct).unwrap();
+        let ty = tcx.lift(ty).unwrap();
+        pretty_print_const_value_tcx(tcx, ct, ty, fmt)
+    })
 }
 
+///////////////////////////////////////////////////////////////////////////
+// Miscellaneous
+
 /// Calc converted u64 decimal into hex and return it's length in chars
 ///
 /// ```ignore (cannot-test-private-function)
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 0c80610b308..c157b7052ab 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -1,6 +1,5 @@
 //! Values computed by queries that use MIR.
 
-use crate::mir::interpret::ConstValue;
 use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::UnordSet;
@@ -16,7 +15,7 @@ use smallvec::SmallVec;
 use std::cell::Cell;
 use std::fmt::{self, Debug};
 
-use super::SourceInfo;
+use super::{ConstValue, SourceInfo};
 
 #[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]
 pub enum UnsafetyViolationKind {
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
new file mode 100644
index 00000000000..25534f46960
--- /dev/null
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -0,0 +1,441 @@
+/// Functionality for statements, operands, places, and things that appear in them.
+use super::*;
+
+///////////////////////////////////////////////////////////////////////////
+// Statements
+
+/// A statement in a basic block, including information about its source code.
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+pub struct Statement<'tcx> {
+    pub source_info: SourceInfo,
+    pub kind: StatementKind<'tcx>,
+}
+
+impl Statement<'_> {
+    /// Changes a statement to a nop. This is both faster than deleting instructions and avoids
+    /// invalidating statement indices in `Location`s.
+    pub fn make_nop(&mut self) {
+        self.kind = StatementKind::Nop
+    }
+
+    /// Changes a statement to a nop and returns the original statement.
+    #[must_use = "If you don't need the statement, use `make_nop` instead"]
+    pub fn replace_nop(&mut self) -> Self {
+        Statement {
+            source_info: self.source_info,
+            kind: mem::replace(&mut self.kind, StatementKind::Nop),
+        }
+    }
+}
+
+impl<'tcx> StatementKind<'tcx> {
+    pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
+        match self {
+            StatementKind::Assign(x) => Some(x),
+            _ => None,
+        }
+    }
+
+    pub fn as_assign(&self) -> Option<&(Place<'tcx>, Rvalue<'tcx>)> {
+        match self {
+            StatementKind::Assign(x) => Some(x),
+            _ => None,
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Places
+
+impl<V, T> ProjectionElem<V, T> {
+    /// Returns `true` if the target of this projection may refer to a different region of memory
+    /// than the base.
+    fn is_indirect(&self) -> bool {
+        match self {
+            Self::Deref => true,
+
+            Self::Field(_, _)
+            | Self::Index(_)
+            | Self::OpaqueCast(_)
+            | Self::ConstantIndex { .. }
+            | Self::Subslice { .. }
+            | Self::Downcast(_, _) => false,
+        }
+    }
+
+    /// Returns `true` if the target of this projection always refers to the same memory region
+    /// whatever the state of the program.
+    pub fn is_stable_offset(&self) -> bool {
+        match self {
+            Self::Deref | Self::Index(_) => false,
+            Self::Field(_, _)
+            | Self::OpaqueCast(_)
+            | Self::ConstantIndex { .. }
+            | Self::Subslice { .. }
+            | Self::Downcast(_, _) => true,
+        }
+    }
+
+    /// Returns `true` if this is a `Downcast` projection with the given `VariantIdx`.
+    pub fn is_downcast_to(&self, v: VariantIdx) -> bool {
+        matches!(*self, Self::Downcast(_, x) if x == v)
+    }
+
+    /// Returns `true` if this is a `Field` projection with the given index.
+    pub fn is_field_to(&self, f: FieldIdx) -> bool {
+        matches!(*self, Self::Field(x, _) if x == f)
+    }
+
+    /// Returns `true` if this is accepted inside `VarDebugInfoContents::Place`.
+    pub fn can_use_in_debuginfo(&self) -> bool {
+        match self {
+            Self::ConstantIndex { from_end: false, .. }
+            | Self::Deref
+            | Self::Downcast(_, _)
+            | Self::Field(_, _) => true,
+            Self::ConstantIndex { from_end: true, .. }
+            | Self::Index(_)
+            | Self::OpaqueCast(_)
+            | Self::Subslice { .. } => false,
+        }
+    }
+}
+
+/// Alias for projections as they appear in `UserTypeProjection`, where we
+/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
+pub type ProjectionKind = ProjectionElem<(), ()>;
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct PlaceRef<'tcx> {
+    pub local: Local,
+    pub projection: &'tcx [PlaceElem<'tcx>],
+}
+
+// Once we stop implementing `Ord` for `DefId`,
+// this impl will be unnecessary. Until then, we'll
+// leave this impl in place to prevent re-adding a
+// dependency on the `Ord` impl for `DefId`
+impl<'tcx> !PartialOrd for PlaceRef<'tcx> {}
+
+impl<'tcx> Place<'tcx> {
+    // FIXME change this to a const fn by also making List::empty a const fn.
+    pub fn return_place() -> Place<'tcx> {
+        Place { local: RETURN_PLACE, projection: List::empty() }
+    }
+
+    /// Returns `true` if this `Place` contains a `Deref` projection.
+    ///
+    /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
+    /// same region of memory as its base.
+    pub fn is_indirect(&self) -> bool {
+        self.projection.iter().any(|elem| elem.is_indirect())
+    }
+
+    /// Returns `true` if this `Place`'s first projection is `Deref`.
+    ///
+    /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
+    /// `Deref` projections can only occur as the first projection. In that case this method
+    /// is equivalent to `is_indirect`, but faster.
+    pub fn is_indirect_first_projection(&self) -> bool {
+        self.as_ref().is_indirect_first_projection()
+    }
+
+    /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
+    /// a single deref of a local.
+    #[inline(always)]
+    pub fn local_or_deref_local(&self) -> Option<Local> {
+        self.as_ref().local_or_deref_local()
+    }
+
+    /// If this place represents a local variable like `_X` with no
+    /// projections, return `Some(_X)`.
+    #[inline(always)]
+    pub fn as_local(&self) -> Option<Local> {
+        self.as_ref().as_local()
+    }
+
+    #[inline]
+    pub fn as_ref(&self) -> PlaceRef<'tcx> {
+        PlaceRef { local: self.local, projection: &self.projection }
+    }
+
+    /// Iterate over the projections in evaluation order, i.e., the first element is the base with
+    /// its projection and then subsequently more projections are added.
+    /// As a concrete example, given the place a.b.c, this would yield:
+    /// - (a, .b)
+    /// - (a.b, .c)
+    ///
+    /// Given a place without projections, the iterator is empty.
+    #[inline]
+    pub fn iter_projections(
+        self,
+    ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
+        self.as_ref().iter_projections()
+    }
+
+    /// Generates a new place by appending `more_projections` to the existing ones
+    /// and interning the result.
+    pub fn project_deeper(self, more_projections: &[PlaceElem<'tcx>], tcx: TyCtxt<'tcx>) -> Self {
+        if more_projections.is_empty() {
+            return self;
+        }
+
+        self.as_ref().project_deeper(more_projections, tcx)
+    }
+}
+
+impl From<Local> for Place<'_> {
+    #[inline]
+    fn from(local: Local) -> Self {
+        Place { local, projection: List::empty() }
+    }
+}
+
+impl<'tcx> PlaceRef<'tcx> {
+    /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
+    /// a single deref of a local.
+    pub fn local_or_deref_local(&self) -> Option<Local> {
+        match *self {
+            PlaceRef { local, projection: [] }
+            | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local),
+            _ => None,
+        }
+    }
+
+    /// Returns `true` if this `Place` contains a `Deref` projection.
+    ///
+    /// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
+    /// same region of memory as its base.
+    pub fn is_indirect(&self) -> bool {
+        self.projection.iter().any(|elem| elem.is_indirect())
+    }
+
+    /// Returns `true` if this `Place`'s first projection is `Deref`.
+    ///
+    /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later,
+    /// `Deref` projections can only occur as the first projection. In that case this method
+    /// is equivalent to `is_indirect`, but faster.
+    pub fn is_indirect_first_projection(&self) -> bool {
+        // To make sure this is not accidentally used in wrong mir phase
+        debug_assert!(
+            self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref)
+        );
+        self.projection.first() == Some(&PlaceElem::Deref)
+    }
+
+    /// If this place represents a local variable like `_X` with no
+    /// projections, return `Some(_X)`.
+    #[inline]
+    pub fn as_local(&self) -> Option<Local> {
+        match *self {
+            PlaceRef { local, projection: [] } => Some(local),
+            _ => None,
+        }
+    }
+
+    #[inline]
+    pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> {
+        if let &[ref proj_base @ .., elem] = self.projection {
+            Some((PlaceRef { local: self.local, projection: proj_base }, elem))
+        } else {
+            None
+        }
+    }
+
+    /// Iterate over the projections in evaluation order, i.e., the first element is the base with
+    /// its projection and then subsequently more projections are added.
+    /// As a concrete example, given the place a.b.c, this would yield:
+    /// - (a, .b)
+    /// - (a.b, .c)
+    ///
+    /// Given a place without projections, the iterator is empty.
+    #[inline]
+    pub fn iter_projections(
+        self,
+    ) -> impl Iterator<Item = (PlaceRef<'tcx>, PlaceElem<'tcx>)> + DoubleEndedIterator {
+        self.projection.iter().enumerate().map(move |(i, proj)| {
+            let base = PlaceRef { local: self.local, projection: &self.projection[..i] };
+            (base, *proj)
+        })
+    }
+
+    /// Generates a new place by appending `more_projections` to the existing ones
+    /// and interning the result.
+    pub fn project_deeper(
+        self,
+        more_projections: &[PlaceElem<'tcx>],
+        tcx: TyCtxt<'tcx>,
+    ) -> Place<'tcx> {
+        let mut v: Vec<PlaceElem<'tcx>>;
+
+        let new_projections = if self.projection.is_empty() {
+            more_projections
+        } else {
+            v = Vec::with_capacity(self.projection.len() + more_projections.len());
+            v.extend(self.projection);
+            v.extend(more_projections);
+            &v
+        };
+
+        Place { local: self.local, projection: tcx.mk_place_elems(new_projections) }
+    }
+}
+
+impl From<Local> for PlaceRef<'_> {
+    #[inline]
+    fn from(local: Local) -> Self {
+        PlaceRef { local, projection: &[] }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Operands
+
+impl<'tcx> Operand<'tcx> {
+    /// Convenience helper to make a constant that refers to the fn
+    /// with given `DefId` and args. Since this is used to synthesize
+    /// MIR, assumes `user_ty` is None.
+    pub fn function_handle(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        args: impl IntoIterator<Item = GenericArg<'tcx>>,
+        span: Span,
+    ) -> Self {
+        let ty = Ty::new_fn_def(tcx, def_id, args);
+        Operand::Constant(Box::new(Constant {
+            span,
+            user_ty: None,
+            literal: ConstantKind::Val(ConstValue::ZeroSized, ty),
+        }))
+    }
+
+    pub fn is_move(&self) -> bool {
+        matches!(self, Operand::Move(..))
+    }
+
+    /// Convenience helper to make a literal-like constant from a given scalar value.
+    /// Since this is used to synthesize MIR, assumes `user_ty` is None.
+    pub fn const_from_scalar(
+        tcx: TyCtxt<'tcx>,
+        ty: Ty<'tcx>,
+        val: Scalar,
+        span: Span,
+    ) -> Operand<'tcx> {
+        debug_assert!({
+            let param_env_and_ty = ty::ParamEnv::empty().and(ty);
+            let type_size = tcx
+                .layout_of(param_env_and_ty)
+                .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}"))
+                .size;
+            let scalar_size = match val {
+                Scalar::Int(int) => int.size(),
+                _ => panic!("Invalid scalar type {val:?}"),
+            };
+            scalar_size == type_size
+        });
+        Operand::Constant(Box::new(Constant {
+            span,
+            user_ty: None,
+            literal: ConstantKind::Val(ConstValue::Scalar(val), ty),
+        }))
+    }
+
+    pub fn to_copy(&self) -> Self {
+        match *self {
+            Operand::Copy(_) | Operand::Constant(_) => self.clone(),
+            Operand::Move(place) => Operand::Copy(place),
+        }
+    }
+
+    /// Returns the `Place` that is the target of this `Operand`, or `None` if this `Operand` is a
+    /// constant.
+    pub fn place(&self) -> Option<Place<'tcx>> {
+        match self {
+            Operand::Copy(place) | Operand::Move(place) => Some(*place),
+            Operand::Constant(_) => None,
+        }
+    }
+
+    /// Returns the `Constant` that is the target of this `Operand`, or `None` if this `Operand` is a
+    /// place.
+    pub fn constant(&self) -> Option<&Constant<'tcx>> {
+        match self {
+            Operand::Constant(x) => Some(&**x),
+            Operand::Copy(_) | Operand::Move(_) => None,
+        }
+    }
+
+    /// Gets the `ty::FnDef` from an operand if it's a constant function item.
+    ///
+    /// While this is unlikely in general, it's the normal case of what you'll
+    /// find as the `func` in a [`TerminatorKind::Call`].
+    pub fn const_fn_def(&self) -> Option<(DefId, GenericArgsRef<'tcx>)> {
+        let const_ty = self.constant()?.literal.ty();
+        if let ty::FnDef(def_id, args) = *const_ty.kind() { Some((def_id, args)) } else { None }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+/// Rvalues
+
+impl<'tcx> Rvalue<'tcx> {
+    /// Returns true if rvalue can be safely removed when the result is unused.
+    #[inline]
+    pub fn is_safe_to_remove(&self) -> bool {
+        match self {
+            // Pointer to int casts may be side-effects due to exposing the provenance.
+            // While the model is undecided, we should be conservative. See
+            // <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
+            Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
+
+            Rvalue::Use(_)
+            | Rvalue::CopyForDeref(_)
+            | Rvalue::Repeat(_, _)
+            | Rvalue::Ref(_, _, _)
+            | Rvalue::ThreadLocalRef(_)
+            | Rvalue::AddressOf(_, _)
+            | Rvalue::Len(_)
+            | Rvalue::Cast(
+                CastKind::IntToInt
+                | CastKind::FloatToInt
+                | CastKind::FloatToFloat
+                | CastKind::IntToFloat
+                | CastKind::FnPtrToPtr
+                | CastKind::PtrToPtr
+                | CastKind::PointerCoercion(_)
+                | CastKind::PointerFromExposedAddress
+                | CastKind::DynStar
+                | CastKind::Transmute,
+                _,
+                _,
+            )
+            | Rvalue::BinaryOp(_, _)
+            | Rvalue::CheckedBinaryOp(_, _)
+            | Rvalue::NullaryOp(_, _)
+            | Rvalue::UnaryOp(_, _)
+            | Rvalue::Discriminant(_)
+            | Rvalue::Aggregate(_, _)
+            | Rvalue::ShallowInitBox(_, _) => true,
+        }
+    }
+}
+
+impl BorrowKind {
+    pub fn mutability(&self) -> Mutability {
+        match *self {
+            BorrowKind::Shared | BorrowKind::Shallow => Mutability::Not,
+            BorrowKind::Mut { .. } => Mutability::Mut,
+        }
+    }
+
+    pub fn allows_two_phase_borrow(&self) -> bool {
+        match *self {
+            BorrowKind::Shared
+            | BorrowKind::Shallow
+            | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
+                false
+            }
+            BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 79b64a491f4..f99084d0eb7 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -3,7 +3,7 @@
 //! This is in a dedicated file so that changes to this file can be reviewed more carefully.
 //! The intention is that this file only contains datatype declarations, no code.
 
-use super::{BasicBlock, Constant, Local, SwitchTargets, UserTypeProjection};
+use super::{BasicBlock, Constant, Local, UserTypeProjection};
 
 use crate::mir::coverage::{CodeRegion, CoverageKind};
 use crate::traits::Reveal;
@@ -24,6 +24,7 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
 use rustc_target::asm::InlineAsmRegOrRegClass;
+use smallvec::SmallVec;
 
 /// Represents the "flavors" of MIR.
 ///
@@ -828,6 +829,27 @@ impl TerminatorKind<'_> {
     }
 }
 
+#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
+pub struct SwitchTargets {
+    /// Possible values. The locations to branch to in each case
+    /// are found in the corresponding indices from the `targets` vector.
+    pub(super) values: SmallVec<[u128; 1]>,
+
+    /// Possible branch sites. The last element of this vector is used
+    /// for the otherwise branch, so targets.len() == values.len() + 1
+    /// should hold.
+    //
+    // This invariant is quite non-obvious and also could be improved.
+    // One way to make this invariant is to have something like this instead:
+    //
+    // branches: Vec<(ConstInt, BasicBlock)>,
+    // otherwise: Option<BasicBlock> // exhaustive if None
+    //
+    // However we’ve decided to keep this as-is until we figure a case
+    // where some other approach seems to be strictly better than other.
+    pub(super) targets: SmallVec<[BasicBlock; 2]>,
+}
+
 /// Action to be taken when a stack unwind happens.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
 #[derive(TypeFoldable, TypeVisitable)]
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 7eddf13b3fb..02aab4a892d 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -1,39 +1,16 @@
+/// Functionality for terminators and helper types that appear in terminators.
 use rustc_hir::LangItem;
 use smallvec::SmallVec;
 
 use super::{BasicBlock, InlineAsmOperand, Operand, SourceInfo, TerminatorKind, UnwindAction};
-use rustc_ast::InlineAsmTemplatePiece;
 pub use rustc_ast::Mutability;
 use rustc_macros::HashStable;
-use std::borrow::Cow;
-use std::fmt::{self, Debug, Formatter, Write};
 use std::iter;
 use std::slice;
 
 pub use super::query::*;
 use super::*;
 
-#[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
-pub struct SwitchTargets {
-    /// Possible values. The locations to branch to in each case
-    /// are found in the corresponding indices from the `targets` vector.
-    values: SmallVec<[u128; 1]>,
-
-    /// Possible branch sites. The last element of this vector is used
-    /// for the otherwise branch, so targets.len() == values.len() + 1
-    /// should hold.
-    //
-    // This invariant is quite non-obvious and also could be improved.
-    // One way to make this invariant is to have something like this instead:
-    //
-    // branches: Vec<(ConstInt, BasicBlock)>,
-    // otherwise: Option<BasicBlock> // exhaustive if None
-    //
-    // However we’ve decided to keep this as-is until we figure a case
-    // where some other approach seems to be strictly better than other.
-    targets: SmallVec<[BasicBlock; 2]>,
-}
-
 impl SwitchTargets {
     /// Creates switch targets from an iterator of values and target blocks.
     ///
@@ -135,6 +112,168 @@ impl UnwindTerminateReason {
     }
 }
 
+impl<O> AssertKind<O> {
+    /// Returns true if this an overflow checking assertion controlled by -C overflow-checks.
+    pub fn is_optional_overflow_check(&self) -> bool {
+        use AssertKind::*;
+        use BinOp::*;
+        matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
+    }
+
+    /// Get the message that is printed at runtime when this assertion fails.
+    ///
+    /// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
+    /// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
+    /// instead of printing a static message.
+    pub fn description(&self) -> &'static str {
+        use AssertKind::*;
+        match self {
+            Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
+            Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
+            Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
+            Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
+            Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
+            OverflowNeg(_) => "attempt to negate with overflow",
+            Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
+            Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
+            Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
+            DivisionByZero(_) => "attempt to divide by zero",
+            RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
+            ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
+            ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
+            ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
+            ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
+            BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
+                bug!("Unexpected AssertKind")
+            }
+        }
+    }
+
+    /// Format the message arguments for the `assert(cond, msg..)` terminator in MIR printing.
+    ///
+    /// Needs to be kept in sync with the run-time behavior (which is defined by
+    /// `AssertKind::description` and the lang items mentioned in its docs).
+    /// Note that we deliberately show more details here than we do at runtime, such as the actual
+    /// numbers that overflowed -- it is much easier to do so here than at runtime.
+    pub fn fmt_assert_args<W: fmt::Write>(&self, f: &mut W) -> fmt::Result
+    where
+        O: Debug,
+    {
+        use AssertKind::*;
+        match self {
+            BoundsCheck { ref len, ref index } => write!(
+                f,
+                "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}"
+            ),
+
+            OverflowNeg(op) => {
+                write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}")
+            }
+            DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"),
+            RemainderByZero(op) => write!(
+                f,
+                "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}"
+            ),
+            Overflow(BinOp::Add, l, r) => write!(
+                f,
+                "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}"
+            ),
+            Overflow(BinOp::Sub, l, r) => write!(
+                f,
+                "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}"
+            ),
+            Overflow(BinOp::Mul, l, r) => write!(
+                f,
+                "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}"
+            ),
+            Overflow(BinOp::Div, l, r) => write!(
+                f,
+                "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}"
+            ),
+            Overflow(BinOp::Rem, l, r) => write!(
+                f,
+                "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}"
+            ),
+            Overflow(BinOp::Shr, _, r) => {
+                write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}")
+            }
+            Overflow(BinOp::Shl, _, r) => {
+                write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
+            }
+            MisalignedPointerDereference { required, found } => {
+                write!(
+                    f,
+                    "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
+                )
+            }
+            _ => write!(f, "\"{}\"", self.description()),
+        }
+    }
+
+    /// Format the diagnostic message for use in a lint (e.g. when the assertion fails during const-eval).
+    ///
+    /// Needs to be kept in sync with the run-time behavior (which is defined by
+    /// `AssertKind::description` and the lang items mentioned in its docs).
+    /// Note that we deliberately show more details here than we do at runtime, such as the actual
+    /// numbers that overflowed -- it is much easier to do so here than at runtime.
+    pub fn diagnostic_message(&self) -> DiagnosticMessage {
+        use crate::fluent_generated::*;
+        use AssertKind::*;
+
+        match self {
+            BoundsCheck { .. } => middle_bounds_check,
+            Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
+            Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
+            Overflow(_, _, _) => middle_assert_op_overflow,
+            OverflowNeg(_) => middle_assert_overflow_neg,
+            DivisionByZero(_) => middle_assert_divide_by_zero,
+            RemainderByZero(_) => middle_assert_remainder_by_zero,
+            ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
+            ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
+            ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
+            ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
+
+            MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
+        }
+    }
+
+    pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
+    where
+        O: fmt::Debug,
+    {
+        use AssertKind::*;
+
+        macro_rules! add {
+            ($name: expr, $value: expr) => {
+                adder($name.into(), $value.into_diagnostic_arg());
+            };
+        }
+
+        match self {
+            BoundsCheck { len, index } => {
+                add!("len", format!("{len:?}"));
+                add!("index", format!("{index:?}"));
+            }
+            Overflow(BinOp::Shl | BinOp::Shr, _, val)
+            | DivisionByZero(val)
+            | RemainderByZero(val)
+            | OverflowNeg(val) => {
+                add!("val", format!("{val:#?}"));
+            }
+            Overflow(binop, left, right) => {
+                add!("op", binop.to_hir_binop().as_str());
+                add!("left", format!("{left:#?}"));
+                add!("right", format!("{right:#?}"));
+            }
+            ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
+            MisalignedPointerDereference { required, found } => {
+                add!("required", format!("{required:#?}"));
+                add!("found", format!("{found:#?}"));
+            }
+        }
+    }
+}
+
 #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub struct Terminator<'tcx> {
     pub source_info: SourceInfo,
@@ -299,187 +438,6 @@ impl<'tcx> TerminatorKind<'tcx> {
     }
 }
 
-impl<'tcx> Debug for TerminatorKind<'tcx> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        self.fmt_head(fmt)?;
-        let successor_count = self.successors().count();
-        let labels = self.fmt_successor_labels();
-        assert_eq!(successor_count, labels.len());
-
-        // `Cleanup` is already included in successors
-        let show_unwind = !matches!(self.unwind(), None | Some(UnwindAction::Cleanup(_)));
-        let fmt_unwind = |fmt: &mut Formatter<'_>| -> fmt::Result {
-            write!(fmt, "unwind ")?;
-            match self.unwind() {
-                // Not needed or included in successors
-                None | Some(UnwindAction::Cleanup(_)) => unreachable!(),
-                Some(UnwindAction::Continue) => write!(fmt, "continue"),
-                Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"),
-                Some(UnwindAction::Terminate(reason)) => {
-                    write!(fmt, "terminate({})", reason.as_short_str())
-                }
-            }
-        };
-
-        match (successor_count, show_unwind) {
-            (0, false) => Ok(()),
-            (0, true) => {
-                write!(fmt, " -> ")?;
-                fmt_unwind(fmt)
-            }
-            (1, false) => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
-            _ => {
-                write!(fmt, " -> [")?;
-                for (i, target) in self.successors().enumerate() {
-                    if i > 0 {
-                        write!(fmt, ", ")?;
-                    }
-                    write!(fmt, "{}: {:?}", labels[i], target)?;
-                }
-                if show_unwind {
-                    write!(fmt, ", ")?;
-                    fmt_unwind(fmt)?;
-                }
-                write!(fmt, "]")
-            }
-        }
-    }
-}
-
-impl<'tcx> TerminatorKind<'tcx> {
-    /// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
-    /// successor basic block, if any. The only information not included is the list of possible
-    /// successors, which may be rendered differently between the text and the graphviz format.
-    pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
-        use self::TerminatorKind::*;
-        match self {
-            Goto { .. } => write!(fmt, "goto"),
-            SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"),
-            Return => write!(fmt, "return"),
-            GeneratorDrop => write!(fmt, "generator_drop"),
-            UnwindResume => write!(fmt, "resume"),
-            UnwindTerminate(reason) => {
-                write!(fmt, "abort({})", reason.as_short_str())
-            }
-            Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"),
-            Unreachable => write!(fmt, "unreachable"),
-            Drop { place, .. } => write!(fmt, "drop({place:?})"),
-            Call { func, args, destination, .. } => {
-                write!(fmt, "{destination:?} = ")?;
-                write!(fmt, "{func:?}(")?;
-                for (index, arg) in args.iter().enumerate() {
-                    if index > 0 {
-                        write!(fmt, ", ")?;
-                    }
-                    write!(fmt, "{arg:?}")?;
-                }
-                write!(fmt, ")")
-            }
-            Assert { cond, expected, msg, .. } => {
-                write!(fmt, "assert(")?;
-                if !expected {
-                    write!(fmt, "!")?;
-                }
-                write!(fmt, "{cond:?}, ")?;
-                msg.fmt_assert_args(fmt)?;
-                write!(fmt, ")")
-            }
-            FalseEdge { .. } => write!(fmt, "falseEdge"),
-            FalseUnwind { .. } => write!(fmt, "falseUnwind"),
-            InlineAsm { template, ref operands, options, .. } => {
-                write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
-                for op in operands {
-                    write!(fmt, ", ")?;
-                    let print_late = |&late| if late { "late" } else { "" };
-                    match op {
-                        InlineAsmOperand::In { reg, value } => {
-                            write!(fmt, "in({reg}) {value:?}")?;
-                        }
-                        InlineAsmOperand::Out { reg, late, place: Some(place) } => {
-                            write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
-                        }
-                        InlineAsmOperand::Out { reg, late, place: None } => {
-                            write!(fmt, "{}out({}) _", print_late(late), reg)?;
-                        }
-                        InlineAsmOperand::InOut {
-                            reg,
-                            late,
-                            in_value,
-                            out_place: Some(out_place),
-                        } => {
-                            write!(
-                                fmt,
-                                "in{}out({}) {:?} => {:?}",
-                                print_late(late),
-                                reg,
-                                in_value,
-                                out_place
-                            )?;
-                        }
-                        InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
-                            write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
-                        }
-                        InlineAsmOperand::Const { value } => {
-                            write!(fmt, "const {value:?}")?;
-                        }
-                        InlineAsmOperand::SymFn { value } => {
-                            write!(fmt, "sym_fn {value:?}")?;
-                        }
-                        InlineAsmOperand::SymStatic { def_id } => {
-                            write!(fmt, "sym_static {def_id:?}")?;
-                        }
-                    }
-                }
-                write!(fmt, ", options({options:?}))")
-            }
-        }
-    }
-
-    /// Returns the list of labels for the edges to the successor basic blocks.
-    pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
-        use self::TerminatorKind::*;
-        match *self {
-            Return | UnwindResume | UnwindTerminate(_) | Unreachable | GeneratorDrop => vec![],
-            Goto { .. } => vec!["".into()],
-            SwitchInt { ref targets, .. } => targets
-                .values
-                .iter()
-                .map(|&u| Cow::Owned(u.to_string()))
-                .chain(iter::once("otherwise".into()))
-                .collect(),
-            Call { target: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
-                vec!["return".into(), "unwind".into()]
-            }
-            Call { target: Some(_), unwind: _, .. } => vec!["return".into()],
-            Call { target: None, unwind: UnwindAction::Cleanup(_), .. } => vec!["unwind".into()],
-            Call { target: None, unwind: _, .. } => vec![],
-            Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
-            Yield { drop: None, .. } => vec!["resume".into()],
-            Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()],
-            Drop { unwind: _, .. } => vec!["return".into()],
-            Assert { unwind: UnwindAction::Cleanup(_), .. } => {
-                vec!["success".into(), "unwind".into()]
-            }
-            Assert { unwind: _, .. } => vec!["success".into()],
-            FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
-            FalseUnwind { unwind: UnwindAction::Cleanup(_), .. } => {
-                vec!["real".into(), "unwind".into()]
-            }
-            FalseUnwind { unwind: _, .. } => vec!["real".into()],
-            InlineAsm { destination: Some(_), unwind: UnwindAction::Cleanup(_), .. } => {
-                vec!["return".into(), "unwind".into()]
-            }
-            InlineAsm { destination: Some(_), unwind: _, .. } => {
-                vec!["return".into()]
-            }
-            InlineAsm { destination: None, unwind: UnwindAction::Cleanup(_), .. } => {
-                vec!["unwind".into()]
-            }
-            InlineAsm { destination: None, unwind: _, .. } => vec![],
-        }
-    }
-}
-
 #[derive(Copy, Clone, Debug)]
 pub enum TerminatorEdges<'mir, 'tcx> {
     /// For terminators that have no successor, like `return`.
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 9b666222ad0..247fcd20c6c 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -121,16 +121,12 @@ impl EraseType for Result<mir::ConstantKind<'_>, mir::interpret::LitToConstError
         [u8; size_of::<Result<mir::ConstantKind<'static>, mir::interpret::LitToConstError>>()];
 }
 
-impl EraseType for Result<mir::interpret::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
-    type Result = [u8; size_of::<
-        Result<mir::interpret::ConstAlloc<'static>, mir::interpret::ErrorHandled>,
-    >()];
+impl EraseType for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
+    type Result = [u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()];
 }
 
-impl EraseType for Result<mir::interpret::ConstValue<'_>, mir::interpret::ErrorHandled> {
-    type Result = [u8; size_of::<
-        Result<mir::interpret::ConstValue<'static>, mir::interpret::ErrorHandled>,
-    >()];
+impl EraseType for Result<mir::ConstValue<'_>, mir::interpret::ErrorHandled> {
+    type Result = [u8; size_of::<Result<mir::ConstValue<'static>, mir::interpret::ErrorHandled>>()];
 }
 
 impl EraseType for Result<Option<ty::ValTree<'_>>, mir::interpret::ErrorHandled> {
@@ -317,8 +313,8 @@ tcx_lifetime! {
     rustc_middle::middle::exported_symbols::ExportedSymbol,
     rustc_middle::mir::ConstantKind,
     rustc_middle::mir::DestructuredConstant,
-    rustc_middle::mir::interpret::ConstAlloc,
-    rustc_middle::mir::interpret::ConstValue,
+    rustc_middle::mir::ConstAlloc,
+    rustc_middle::mir::ConstValue,
     rustc_middle::mir::interpret::GlobalId,
     rustc_middle::mir::interpret::LitToConstInput,
     rustc_middle::traits::query::MethodAutoderefStepsResult,
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 01bdc4c9904..af4c57e702f 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -2,7 +2,6 @@
 
 use crate::infer::canonical::Canonical;
 use crate::mir;
-use crate::mir::interpret::ConstValue;
 use crate::traits;
 use crate::ty::fast_reject::SimplifiedType;
 use crate::ty::layout::{TyAndLayout, ValidityRequirement};
@@ -369,7 +368,7 @@ impl<'tcx> Key for (ty::Const<'tcx>, FieldIdx) {
     }
 }
 
-impl<'tcx> Key for (ConstValue<'tcx>, Ty<'tcx>) {
+impl<'tcx> Key for (mir::ConstValue<'tcx>, Ty<'tcx>) {
     type CacheSelector = DefaultCacheSelector<Self>;
 
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
@@ -377,7 +376,7 @@ impl<'tcx> Key for (ConstValue<'tcx>, Ty<'tcx>) {
     }
 }
 
-impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
+impl<'tcx> Key for mir::ConstAlloc<'tcx> {
     type CacheSelector = DefaultCacheSelector<Self>;
 
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 9e358ea4eba..b5529568ff2 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -21,7 +21,7 @@ use crate::middle::stability::{self, DeprecationEntry};
 use crate::mir;
 use crate::mir::interpret::GlobalId;
 use crate::mir::interpret::{
-    ConstValue, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult,
+    EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult,
 };
 use crate::mir::interpret::{LitToConstError, LitToConstInput};
 use crate::mir::mono::CodegenUnit;
@@ -1091,7 +1091,7 @@ rustc_queries! {
     }
 
     /// Converts a type level constant value into `ConstValue`
-    query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> ConstValue<'tcx> {
+    query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> {
         desc { "converting type-level constant value to mir constant value"}
     }
 
@@ -1104,14 +1104,14 @@ rustc_queries! {
     /// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index
     /// and its field values. This should only be used for pretty printing.
     query try_destructure_mir_constant_for_diagnostics(
-        key: (ConstValue<'tcx>, Ty<'tcx>)
+        key: (mir::ConstValue<'tcx>, Ty<'tcx>)
     ) -> Option<mir::DestructuredConstant<'tcx>> {
         desc { "destructuring MIR constant"}
         no_hash
         eval_always
     }
 
-    query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
+    query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> mir::ConstValue<'tcx> {
         desc { "getting a &core::panic::Location referring to a span" }
     }
 
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index c3ed40867cf..e7e40bee6b9 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -1,30 +1,70 @@
+//! Data structure used to inspect trait solver behavior.
+//!
+//! During trait solving we optionally build "proof trees", the root of
+//! which is a [GoalEvaluation] with [GoalEvaluationKind::Root]. These
+//! trees are used to improve the debug experience and are also used by
+//! the compiler itself to provide necessary context for error messages.
+//!
+//! Because each nested goal in the solver gets [canonicalized] separately
+//! and we discard inference progress via "probes", we cannot mechanically
+//! use proof trees without somehow "lifting up" data local to the current
+//! `InferCtxt`. Any data used mechanically is therefore canonicalized and
+//! stored as [CanonicalState]. As printing canonicalized data worsens the
+//! debugging dumps, we do not simply canonicalize everything.
+//!
+//! This means proof trees contain inference variables and placeholders
+//! local to a different `InferCtxt` which must not be used with the
+//! current one.
+//!
+//! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
+
 use super::{
-    CandidateSource, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput,
-    QueryResult,
+    CandidateSource, Canonical, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution,
+    QueryInput, QueryResult,
 };
-use crate::ty;
+use crate::{infer::canonical::CanonicalVarValues, ty};
 use format::ProofTreeFormatter;
 use std::fmt::{Debug, Write};
 
 mod format;
 
+/// Some `data` together with information about how they relate to the input
+/// of the canonical query.
+///
+/// This is only ever used as [CanonicalState]. Any type information in proof
+/// trees used mechanically has to be canonicalized as we otherwise leak
+/// inference variables from a nested `InferCtxt`.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, TypeFoldable, TypeVisitable)]
+pub struct State<'tcx, T> {
+    pub var_values: CanonicalVarValues<'tcx>,
+    pub data: T,
+}
+
+pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
+
 #[derive(Debug, Eq, PartialEq)]
 pub enum CacheHit {
     Provisional,
     Global,
 }
 
+/// When evaluating the root goals we also store the
+/// original values for the `CanonicalVarValues` of the
+/// canonicalized goal. We use this to map any [CanonicalState]
+/// from the local `InferCtxt` of the solver query to
+/// the `InferCtxt` of the caller.
 #[derive(Eq, PartialEq)]
-pub enum GoalEvaluationKind {
-    Root,
+pub enum GoalEvaluationKind<'tcx> {
+    Root { orig_values: Vec<ty::GenericArg<'tcx>> },
     Nested { is_normalizes_to_hack: IsNormalizesToHack },
 }
 
 #[derive(Eq, PartialEq)]
 pub struct GoalEvaluation<'tcx> {
     pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    pub kind: GoalEvaluationKind,
+    pub kind: GoalEvaluationKind<'tcx>,
     pub evaluation: CanonicalGoalEvaluation<'tcx>,
+    /// The nested goals from instantiating the query response.
     pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
 }
 
@@ -66,6 +106,7 @@ pub struct GoalEvaluationStep<'tcx> {
 /// of a goal.
 #[derive(Eq, PartialEq)]
 pub struct Probe<'tcx> {
+    /// What happened inside of this probe in chronological order.
     pub steps: Vec<ProbeStep<'tcx>>,
     pub kind: ProbeKind<'tcx>,
 }
@@ -78,12 +119,21 @@ impl Debug for Probe<'_> {
 
 #[derive(Eq, PartialEq)]
 pub enum ProbeStep<'tcx> {
-    AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
+    /// We added a goal to the `EvalCtxt` which will get proven
+    /// the next time `EvalCtxt::try_evaluate_added_goals` is called.
+    AddGoal(CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
+    /// The inside of a `EvalCtxt::try_evaluate_added_goals` call.
     EvaluateGoals(AddedGoalsEvaluation<'tcx>),
+    /// A call to `probe` while proving the current goal. This is
+    /// used whenever there are multiple candidates to prove the
+    /// current goalby .
     NestedProbe(Probe<'tcx>),
 }
 
-#[derive(Debug, PartialEq, Eq)]
+/// What kind of probe we're in. In case the probe represents a candidate, or
+/// the final result of the current goal - via [ProbeKind::Root] - we also
+/// store the [QueryResult].
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub enum ProbeKind<'tcx> {
     /// The root inference context while proving a goal.
     Root { result: QueryResult<'tcx> },
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index d33e83ae1ed..5733be00adf 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -41,7 +41,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
 
     pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
         let goal_text = match eval.kind {
-            GoalEvaluationKind::Root => "ROOT GOAL",
+            GoalEvaluationKind::Root { orig_values: _ } => "ROOT GOAL",
             GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack {
                 IsNormalizesToHack::No => "GOAL",
                 IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 9a0e72d7b64..8b425ce0267 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1118,6 +1118,10 @@ where
     fn is_unit(this: TyAndLayout<'tcx>) -> bool {
         matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
     }
+
+    fn is_transparent(this: TyAndLayout<'tcx>) -> bool {
+        matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent())
+    }
 }
 
 /// Calculates whether a function's ABI can unwind or not.
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index eb8ea0bc114..dbff13fdd8e 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -566,6 +566,11 @@ impl rustc_errors::IntoDiagnosticArg for Clause<'_> {
 pub struct Clause<'tcx>(Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>);
 
 impl<'tcx> Clause<'tcx> {
+    pub fn from_projection_clause(tcx: TyCtxt<'tcx>, pred: PolyProjectionPredicate<'tcx>) -> Self {
+        let pred: Predicate<'tcx> = pred.to_predicate(tcx);
+        pred.expect_clause()
+    }
+
     pub fn as_predicate(self) -> Predicate<'tcx> {
         Predicate(self.0)
     }
@@ -1253,14 +1258,6 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for TraitRef<'tcx> {
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for TraitPredicate<'tcx> {
-    #[inline(always)]
-    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
-        let p: Predicate<'tcx> = self.to_predicate(tcx);
-        p.expect_clause()
-    }
-}
-
 impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, TraitRef<'tcx>> {
     #[inline(always)]
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
@@ -1287,18 +1284,6 @@ impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for Binder<'tcx, TraitRef
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for TraitRef<'tcx> {
-    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> PolyTraitPredicate<'tcx> {
-        ty::Binder::dummy(self).to_predicate(tcx)
-    }
-}
-
-impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for TraitPredicate<'tcx> {
-    fn to_predicate(self, _tcx: TyCtxt<'tcx>) -> PolyTraitPredicate<'tcx> {
-        ty::Binder::dummy(self)
-    }
-}
-
 impl<'tcx> ToPredicate<'tcx> for PolyTraitPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         self.map_bound(|p| PredicateKind::Clause(ClauseKind::Trait(p))).to_predicate(tcx)
@@ -1312,12 +1297,6 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyTraitPredicate<'tcx> {
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>> {
-    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
-        ty::Binder::dummy(PredicateKind::Clause(ClauseKind::RegionOutlives(self))).to_predicate(tcx)
-    }
-}
-
 impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         self.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).to_predicate(tcx)
@@ -1330,12 +1309,6 @@ impl<'tcx> ToPredicate<'tcx> for OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>> {
     }
 }
 
-impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
-    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
-        self.map_bound(|p| PredicateKind::Clause(ClauseKind::TypeOutlives(p))).to_predicate(tcx)
-    }
-}
-
 impl<'tcx> ToPredicate<'tcx> for ProjectionPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         ty::Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(self))).to_predicate(tcx)
@@ -1355,13 +1328,6 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for ProjectionPredicate<'tcx> {
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyProjectionPredicate<'tcx> {
-    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
-        let p: Predicate<'tcx> = self.to_predicate(tcx);
-        p.expect_clause()
-    }
-}
-
 impl<'tcx> ToPredicate<'tcx> for TraitPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         PredicateKind::Clause(ClauseKind::Trait(self)).to_predicate(tcx)
@@ -2214,10 +2180,6 @@ impl<'tcx> TyCtxt<'tcx> {
                 // The name of a constructor is that of its parent.
                 rustc_hir::definitions::DefPathData::Ctor => self
                     .opt_item_name(DefId { krate: def_id.krate, index: def_key.parent.unwrap() }),
-                // The name of opaque types only exists in HIR.
-                rustc_hir::definitions::DefPathData::ImplTrait
-                    if let Some(def_id) = def_id.as_local() =>
-                    self.hir().opt_name(self.hir().local_def_id_to_hir_id(def_id)),
                 _ => def_key.get_opt_name(),
             }
         }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index e1d4e43841d..0ee63e92848 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1713,6 +1713,21 @@ pub trait PrettyPrinter<'tcx>:
     }
 }
 
+pub(crate) fn pretty_print_const<'tcx>(
+    c: ty::Const<'tcx>,
+    fmt: &mut fmt::Formatter<'_>,
+    print_types: bool,
+) -> fmt::Result {
+    ty::tls::with(|tcx| {
+        let literal = tcx.lift(c).unwrap();
+        let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+        cx.print_alloc_ids = true;
+        let cx = cx.pretty_print_const(literal, print_types)?;
+        fmt.write_str(&cx.into_buffer())?;
+        Ok(())
+    })
+}
+
 // HACK(eddyb) boxed to avoid moving around a large struct by-value.
 pub struct FmtPrinter<'a, 'tcx>(Box<FmtPrinterData<'a, 'tcx>>);
 
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index f4158597d10..268339ed402 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -457,6 +457,7 @@ TrivialLiftImpls! {
      (),
      bool,
      usize,
+     u64,
 }
 
 // For some things about which the type library does not know, or does not
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index edab7fe58ba..e3e014a3b2a 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -725,7 +725,7 @@ impl<'tcx> PolyExistentialPredicate<'tcx> {
                 self.rebind(tr).with_self_ty(tcx, self_ty).to_predicate(tcx)
             }
             ExistentialPredicate::Projection(p) => {
-                self.rebind(p.with_self_ty(tcx, self_ty)).to_predicate(tcx)
+                ty::Clause::from_projection_clause(tcx, self.rebind(p.with_self_ty(tcx, self_ty)))
             }
             ExistentialPredicate::AutoTrait(did) => {
                 let generics = tcx.generics_of(did);
@@ -2945,6 +2945,33 @@ impl<'tcx> Ty<'tcx> {
             _ => false,
         }
     }
+
+    pub fn is_known_rigid(self) -> bool {
+        match self.kind() {
+            Bool
+            | Char
+            | Int(_)
+            | Uint(_)
+            | Float(_)
+            | Adt(_, _)
+            | Foreign(_)
+            | Str
+            | Array(_, _)
+            | Slice(_)
+            | RawPtr(_)
+            | Ref(_, _, _)
+            | FnDef(_, _)
+            | FnPtr(_)
+            | Dynamic(_, _, _)
+            | Closure(_, _)
+            | Generator(_, _, _)
+            | GeneratorWitness(_)
+            | GeneratorWitnessMIR(_, _)
+            | Never
+            | Tuple(_) => true,
+            Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
+        }
+    }
 }
 
 /// Extra information about why we ended up with a particular variance.
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index da5f2b1bcc9..0eff2df1366 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -1,4 +1,4 @@
-use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::ty::cast::mir_cast_kind;
 use rustc_middle::{mir::*, thir::*, ty};
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index aaa37446e24..d8232199e01 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -3,9 +3,7 @@
 use crate::build::{parse_float_into_constval, Builder};
 use rustc_ast as ast;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::{
-    Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar,
-};
+use rustc_middle::mir::interpret::{Allocation, LitToConstError, LitToConstInput, Scalar};
 use rustc_middle::mir::*;
 use rustc_middle::thir::*;
 use rustc_middle::ty::{
@@ -133,14 +131,14 @@ fn lit_to_mir_constant<'tcx>(
             let s = s.as_str();
             let allocation = Allocation::from_bytes_byte_aligned_immutable(s.as_bytes());
             let allocation = tcx.mk_const_alloc(allocation);
-            ConstValue::Slice { data: allocation, start: 0, end: s.len() }
+            ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
         }
         (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
             if matches!(inner_ty.kind(), ty::Slice(_)) =>
         {
             let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
             let allocation = tcx.mk_const_alloc(allocation);
-            ConstValue::Slice { data: allocation, start: 0, end: data.len() }
+            ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
         }
         (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
             let id = tcx.allocate_bytes(data);
@@ -150,7 +148,7 @@ fn lit_to_mir_constant<'tcx>(
         {
             let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
             let allocation = tcx.mk_const_alloc(allocation);
-            ConstValue::Slice { data: allocation, start: 0, end: data.len() }
+            ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
         }
         (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
             ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 4e10916ad61..7748ffab79c 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -15,7 +15,6 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
 use rustc_middle::middle::region;
-use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::*;
 use rustc_middle::thir::{
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 71e6e6f3a7a..3d9e634c849 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -18,7 +18,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator;
 use rustc_hir::RangeEnd;
 use rustc_index::Idx;
 use rustc_middle::mir::interpret::{
-    ConstValue, ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
+    ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
 };
 use rustc_middle::mir::{self, ConstantKind, UserTypeProjection};
 use rustc_middle::mir::{BorrowKind, Mutability};
@@ -855,8 +855,8 @@ pub(crate) fn compare_const_vals<'tcx>(
         ty::Float(_) | ty::Int(_) => {} // require special handling, see below
         _ => match (a, b) {
             (
-                mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(a)), _a_ty),
-                mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(b)), _b_ty),
+                mir::ConstantKind::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _a_ty),
+                mir::ConstantKind::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _b_ty),
             ) => return Some(a.cmp(&b)),
             (mir::ConstantKind::Ty(a), mir::ConstantKind::Ty(b)) => {
                 return Some(a.kind().cmp(&b.kind()));
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index 4892ace53e3..fe66a9a0994 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem;
 use rustc_index::IndexVec;
 use rustc_middle::mir::*;
 use rustc_middle::mir::{
-    interpret::{ConstValue, Scalar},
+    interpret::Scalar,
     visit::{PlaceContext, Visitor},
 };
 use rustc_middle::ty::{Ty, TyCtxt, TypeAndMut};
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 00e3e3a8f9f..c6aac2ca213 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -22,8 +22,8 @@ use rustc_target::spec::abi::Abi as CallAbi;
 use crate::dataflow_const_prop::Patch;
 use crate::MirPass;
 use rustc_const_eval::interpret::{
-    self, compile_time_machine, AllocId, ConstAllocation, ConstValue, FnArg, Frame, ImmTy,
-    Immediate, InterpCx, InterpResult, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, StackPopCleanup,
+    self, compile_time_machine, AllocId, ConstAllocation, FnArg, Frame, ImmTy, Immediate, InterpCx,
+    InterpResult, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, StackPopCleanup,
 };
 
 /// The maximum number of bytes that we'll allocate space for a local or the return value.
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index 3d442e5dca9..d56d4ad4f1e 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -1,10 +1,8 @@
 use super::Error;
 
-use super::debug;
 use super::graph;
 use super::spans;
 
-use debug::{DebugCounters, NESTED_INDENT};
 use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops};
 use spans::CoverageSpan;
 
@@ -16,6 +14,8 @@ use rustc_middle::mir::coverage::*;
 
 use std::fmt::{self, Debug};
 
+const NESTED_INDENT: &str = "    ";
+
 /// The coverage counter or counter expression associated with a particular
 /// BCB node or BCB edge.
 #[derive(Clone)]
@@ -75,8 +75,6 @@ pub(super) struct CoverageCounters {
     /// BCB/edge, but are needed as operands to more complex expressions.
     /// These are always [`BcbCounter::Expression`].
     pub(super) intermediate_expressions: Vec<BcbCounter>,
-
-    pub debug_counters: DebugCounters,
 }
 
 impl CoverageCounters {
@@ -91,17 +89,9 @@ impl CoverageCounters {
             bcb_edge_counters: FxHashMap::default(),
             bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs),
             intermediate_expressions: Vec::new(),
-
-            debug_counters: DebugCounters::new(),
         }
     }
 
-    /// Activate the `DebugCounters` data structures, to provide additional debug formatting
-    /// features when formatting [`BcbCounter`] (counter) values.
-    pub fn enable_debug(&mut self) {
-        self.debug_counters.enable();
-    }
-
     /// Makes [`BcbCounter`] `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or
     /// indirectly associated with `CoverageSpans`, and accumulates additional `Expression`s
     /// representing intermediate values.
@@ -113,44 +103,18 @@ impl CoverageCounters {
         MakeBcbCounters::new(self, basic_coverage_blocks).make_bcb_counters(coverage_spans)
     }
 
-    fn make_counter<F>(&mut self, debug_block_label_fn: F) -> BcbCounter
-    where
-        F: Fn() -> Option<String>,
-    {
-        let counter = BcbCounter::Counter { id: self.next_counter() };
-        if self.debug_counters.is_enabled() {
-            self.debug_counters.add_counter(&counter, (debug_block_label_fn)());
-        }
-        counter
+    fn make_counter(&mut self) -> BcbCounter {
+        let id = self.next_counter();
+        BcbCounter::Counter { id }
     }
 
-    fn make_expression<F>(
-        &mut self,
-        lhs: Operand,
-        op: Op,
-        rhs: Operand,
-        debug_block_label_fn: F,
-    ) -> BcbCounter
-    where
-        F: Fn() -> Option<String>,
-    {
+    fn make_expression(&mut self, lhs: Operand, op: Op, rhs: Operand) -> BcbCounter {
         let id = self.next_expression();
-        let expression = BcbCounter::Expression { id, lhs, op, rhs };
-        if self.debug_counters.is_enabled() {
-            self.debug_counters.add_counter(&expression, (debug_block_label_fn)());
-        }
-        expression
+        BcbCounter::Expression { id, lhs, op, rhs }
     }
 
     pub fn make_identity_counter(&mut self, counter_operand: Operand) -> BcbCounter {
-        let some_debug_block_label = if self.debug_counters.is_enabled() {
-            self.debug_counters.some_block_label(counter_operand).cloned()
-        } else {
-            None
-        };
-        self.make_expression(counter_operand, Op::Add, Operand::Zero, || {
-            some_debug_block_label.clone()
-        })
+        self.make_expression(counter_operand, Op::Add, Operand::Zero)
     }
 
     /// Counter IDs start from one and go up.
@@ -367,12 +331,8 @@ impl<'a> MakeBcbCounters<'a> {
                         branch_counter_operand,
                         Op::Add,
                         sumup_counter_operand,
-                        || None,
-                    );
-                    debug!(
-                        "  [new intermediate expression: {}]",
-                        self.format_counter(&intermediate_expression)
                     );
+                    debug!("  [new intermediate expression: {:?}]", intermediate_expression);
                     let intermediate_expression_operand = intermediate_expression.as_operand();
                     self.coverage_counters.intermediate_expressions.push(intermediate_expression);
                     some_sumup_counter_operand.replace(intermediate_expression_operand);
@@ -394,9 +354,8 @@ impl<'a> MakeBcbCounters<'a> {
             branching_counter_operand,
             Op::Subtract,
             sumup_counter_operand,
-            || Some(format!("{expression_branch:?}")),
         );
-        debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression));
+        debug!("{:?} gets an expression: {:?}", expression_branch, expression);
         let bcb = expression_branch.target_bcb;
         if expression_branch.is_only_path_to_target() {
             self.coverage_counters.set_bcb_counter(bcb, expression)?;
@@ -418,10 +377,10 @@ impl<'a> MakeBcbCounters<'a> {
         // If the BCB already has a counter, return it.
         if let Some(counter_kind) = &self.coverage_counters.bcb_counters[bcb] {
             debug!(
-                "{}{:?} already has a counter: {}",
+                "{}{:?} already has a counter: {:?}",
                 NESTED_INDENT.repeat(debug_indent_level),
                 bcb,
-                self.format_counter(counter_kind),
+                counter_kind,
             );
             return Ok(counter_kind.as_operand());
         }
@@ -431,22 +390,22 @@ impl<'a> MakeBcbCounters<'a> {
         // program results in a tight infinite loop, but it should still compile.
         let one_path_to_target = self.bcb_has_one_path_to_target(bcb);
         if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) {
-            let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{bcb:?}")));
+            let counter_kind = self.coverage_counters.make_counter();
             if one_path_to_target {
                 debug!(
-                    "{}{:?} gets a new counter: {}",
+                    "{}{:?} gets a new counter: {:?}",
                     NESTED_INDENT.repeat(debug_indent_level),
                     bcb,
-                    self.format_counter(&counter_kind),
+                    counter_kind,
                 );
             } else {
                 debug!(
                     "{}{:?} has itself as its own predecessor. It can't be part of its own \
-                    Expression sum, so it will get its own new counter: {}. (Note, the compiled \
+                    Expression sum, so it will get its own new counter: {:?}. (Note, the compiled \
                     code will generate an infinite loop.)",
                     NESTED_INDENT.repeat(debug_indent_level),
                     bcb,
-                    self.format_counter(&counter_kind),
+                    counter_kind,
                 );
             }
             return self.coverage_counters.set_bcb_counter(bcb, counter_kind);
@@ -481,12 +440,11 @@ impl<'a> MakeBcbCounters<'a> {
                     sumup_edge_counter_operand,
                     Op::Add,
                     edge_counter_operand,
-                    || None,
                 );
                 debug!(
-                    "{}new intermediate expression: {}",
+                    "{}new intermediate expression: {:?}",
                     NESTED_INDENT.repeat(debug_indent_level),
-                    self.format_counter(&intermediate_expression)
+                    intermediate_expression
                 );
                 let intermediate_expression_operand = intermediate_expression.as_operand();
                 self.coverage_counters.intermediate_expressions.push(intermediate_expression);
@@ -497,13 +455,12 @@ impl<'a> MakeBcbCounters<'a> {
             first_edge_counter_operand,
             Op::Add,
             some_sumup_edge_counter_operand.unwrap(),
-            || Some(format!("{bcb:?}")),
         );
         debug!(
-            "{}{:?} gets a new counter (sum of predecessor counters): {}",
+            "{}{:?} gets a new counter (sum of predecessor counters): {:?}",
             NESTED_INDENT.repeat(debug_indent_level),
             bcb,
-            self.format_counter(&counter_kind)
+            counter_kind
         );
         self.coverage_counters.set_bcb_counter(bcb, counter_kind)
     }
@@ -534,24 +491,23 @@ impl<'a> MakeBcbCounters<'a> {
             self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
         {
             debug!(
-                "{}Edge {:?}->{:?} already has a counter: {}",
+                "{}Edge {:?}->{:?} already has a counter: {:?}",
                 NESTED_INDENT.repeat(debug_indent_level),
                 from_bcb,
                 to_bcb,
-                self.format_counter(counter_kind)
+                counter_kind
             );
             return Ok(counter_kind.as_operand());
         }
 
         // Make a new counter to count this edge.
-        let counter_kind =
-            self.coverage_counters.make_counter(|| Some(format!("{from_bcb:?}->{to_bcb:?}")));
+        let counter_kind = self.coverage_counters.make_counter();
         debug!(
-            "{}Edge {:?}->{:?} gets a new counter: {}",
+            "{}Edge {:?}->{:?} gets a new counter: {:?}",
             NESTED_INDENT.repeat(debug_indent_level),
             from_bcb,
             to_bcb,
-            self.format_counter(&counter_kind)
+            counter_kind
         );
         self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind)
     }
@@ -710,9 +666,4 @@ impl<'a> MakeBcbCounters<'a> {
     fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
         self.basic_coverage_blocks.dominates(dom, node)
     }
-
-    #[inline]
-    fn format_counter(&self, counter_kind: &BcbCounter) -> String {
-        self.coverage_counters.debug_counters.format_counter(counter_kind)
-    }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs
deleted file mode 100644
index bb1f16aa8be..00000000000
--- a/compiler/rustc_mir_transform/src/coverage/debug.rs
+++ /dev/null
@@ -1,797 +0,0 @@
-//! The `InstrumentCoverage` MIR pass implementation includes debugging tools and options
-//! to help developers understand and/or improve the analysis and instrumentation of a MIR.
-//!
-//! To enable coverage, include the rustc command line option:
-//!
-//!   * `-C instrument-coverage`
-//!
-//! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview
-//! ------------------------------------------------------------------------------------
-//!
-//! Additional debugging options include:
-//!
-//!   * `-Z dump-mir=InstrumentCoverage` - Generate `.mir` files showing the state of the MIR,
-//!     before and after the `InstrumentCoverage` pass, for each compiled function.
-//!
-//!   * `-Z dump-mir-graphviz` - If `-Z dump-mir` is also enabled for the current MIR node path,
-//!     each MIR dump is accompanied by a before-and-after graphical view of the MIR, in Graphviz
-//!     `.dot` file format (which can be visually rendered as a graph using any of a number of free
-//!     Graphviz viewers and IDE extensions).
-//!
-//!     For the `InstrumentCoverage` pass, this option also enables generation of an additional
-//!     Graphviz `.dot` file for each function, rendering the `CoverageGraph`: the control flow
-//!     graph (CFG) of `BasicCoverageBlocks` (BCBs), as nodes, internally labeled to show the
-//!     `CoverageSpan`-based MIR elements each BCB represents (`BasicBlock`s, `Statement`s and
-//!     `Terminator`s), assigned coverage counters and/or expressions, and edge counters, as needed.
-//!
-//!     (Note the additional option, `-Z graphviz-dark-mode`, can be added, to change the rendered
-//!     output from its default black-on-white background to a dark color theme, if desired.)
-//!
-//!   * `-Z dump-mir-spanview` - If `-Z dump-mir` is also enabled for the current MIR node path,
-//!     each MIR dump is accompanied by a before-and-after `.html` document showing the function's
-//!     original source code, highlighted by it's MIR spans, at the `statement`-level (by default),
-//!     `terminator` only, or encompassing span for the `Terminator` plus all `Statement`s, in each
-//!     `block` (`BasicBlock`).
-//!
-//!     For the `InstrumentCoverage` pass, this option also enables generation of an additional
-//!     spanview `.html` file for each function, showing the aggregated `CoverageSpan`s that will
-//!     require counters (or counter expressions) for accurate coverage analysis.
-//!
-//! Debug Logging
-//! -------------
-//!
-//! The `InstrumentCoverage` pass includes debug logging messages at various phases and decision
-//! points, which can be enabled via environment variable:
-//!
-//! ```shell
-//! RUSTC_LOG=rustc_mir_transform::coverage=debug
-//! ```
-//!
-//! Other module paths with coverage-related debug logs may also be of interest, particularly for
-//! debugging the coverage map data, injected as global variables in the LLVM IR (during rustc's
-//! code generation pass). For example:
-//!
-//! ```shell
-//! RUSTC_LOG=rustc_mir_transform::coverage,rustc_codegen_llvm::coverageinfo=debug
-//! ```
-//!
-//! Coverage Debug Options
-//! ---------------------------------
-//!
-//! Additional debugging options can be enabled using the environment variable:
-//!
-//! ```shell
-//! RUSTC_COVERAGE_DEBUG_OPTIONS=<options>
-//! ```
-//!
-//! These options are comma-separated, and specified in the format `option-name=value`. For example:
-//!
-//! ```shell
-//! $ RUSTC_COVERAGE_DEBUG_OPTIONS=counter-format=id+operation,allow-unused-expressions=yes cargo build
-//! ```
-//!
-//! Coverage debug options include:
-//!
-//!   * `allow-unused-expressions=yes` or `no` (default: `no`)
-//!
-//!     The `InstrumentCoverage` algorithms _should_ only create and assign expressions to a
-//!     `BasicCoverageBlock`, or an incoming edge, if that expression is either (a) required to
-//!     count a `CoverageSpan`, or (b) a dependency of some other required counter expression.
-//!
-//!     If an expression is generated that does not map to a `CoverageSpan` or dependency, this
-//!     probably indicates there was a bug in the algorithm that creates and assigns counters
-//!     and expressions.
-//!
-//!     When this kind of bug is encountered, the rustc compiler will panic by default. Setting:
-//!     `allow-unused-expressions=yes` will log a warning message instead of panicking (effectively
-//!     ignoring the unused expressions), which may be helpful when debugging the root cause of
-//!     the problem.
-//!
-//!   * `counter-format=<choices>`, where `<choices>` can be any plus-separated combination of `id`,
-//!     `block`, and/or `operation` (default: `block+operation`)
-//!
-//!     This option effects both the `CoverageGraph` (graphviz `.dot` files) and debug logging, when
-//!     generating labels for counters and expressions.
-//!
-//!     Depending on the values and combinations, counters can be labeled by:
-//!
-//!       * `id` - counter or expression ID (ascending counter IDs, starting at 1, or descending
-//!         expression IDs, starting at `u32:MAX`)
-//!       * `block` - the `BasicCoverageBlock` label (for example, `bcb0`) or edge label (for
-//!         example `bcb0->bcb1`), for counters or expressions assigned to count a
-//!         `BasicCoverageBlock` or edge. Intermediate expressions (not directly associated with
-//!         a BCB or edge) will be labeled by their expression ID, unless `operation` is also
-//!         specified.
-//!       * `operation` - applied to expressions only, labels include the left-hand-side counter
-//!         or expression label (lhs operand), the operator (`+` or `-`), and the right-hand-side
-//!         counter or expression (rhs operand). Expression operand labels are generated
-//!         recursively, generating labels with nested operations, enclosed in parentheses
-//!         (for example: `bcb2 + (bcb0 - bcb1)`).
-
-use std::iter;
-use std::ops::Deref;
-use std::sync::OnceLock;
-
-use itertools::Itertools;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_middle::mir::coverage::*;
-use rustc_middle::mir::create_dump_file;
-use rustc_middle::mir::generic_graphviz::GraphvizWriter;
-use rustc_middle::mir::spanview::{self, SpanViewable};
-use rustc_middle::mir::{self, BasicBlock};
-use rustc_middle::ty::TyCtxt;
-use rustc_span::Span;
-
-use super::counters::{BcbCounter, CoverageCounters};
-use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
-use super::spans::CoverageSpan;
-
-pub const NESTED_INDENT: &str = "    ";
-
-const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS";
-
-pub(super) fn debug_options<'a>() -> &'a DebugOptions {
-    static DEBUG_OPTIONS: OnceLock<DebugOptions> = OnceLock::new();
-
-    &DEBUG_OPTIONS.get_or_init(DebugOptions::from_env)
-}
-
-/// Parses and maintains coverage-specific debug options captured from the environment variable
-/// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set.
-#[derive(Debug, Clone)]
-pub(super) struct DebugOptions {
-    pub allow_unused_expressions: bool,
-    counter_format: ExpressionFormat,
-}
-
-impl DebugOptions {
-    fn from_env() -> Self {
-        let mut allow_unused_expressions = true;
-        let mut counter_format = ExpressionFormat::default();
-
-        if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) {
-            for setting_str in env_debug_options.replace(' ', "").replace('-', "_").split(',') {
-                let (option, value) = match setting_str.split_once('=') {
-                    None => (setting_str, None),
-                    Some((k, v)) => (k, Some(v)),
-                };
-                match option {
-                    "allow_unused_expressions" => {
-                        allow_unused_expressions = bool_option_val(option, value);
-                        debug!(
-                            "{} env option `allow_unused_expressions` is set to {}",
-                            RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions
-                        );
-                    }
-                    "counter_format" => {
-                        match value {
-                            None => {
-                                bug!(
-                                    "`{}` option in environment variable {} requires one or more \
-                                    plus-separated choices (a non-empty subset of \
-                                    `id+block+operation`)",
-                                    option,
-                                    RUSTC_COVERAGE_DEBUG_OPTIONS
-                                );
-                            }
-                            Some(val) => {
-                                counter_format = counter_format_option_val(val);
-                                debug!(
-                                    "{} env option `counter_format` is set to {:?}",
-                                    RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format
-                                );
-                            }
-                        };
-                    }
-                    _ => bug!(
-                        "Unsupported setting `{}` in environment variable {}",
-                        option,
-                        RUSTC_COVERAGE_DEBUG_OPTIONS
-                    ),
-                };
-            }
-        }
-
-        Self { allow_unused_expressions, counter_format }
-    }
-}
-
-fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool {
-    if let Some(val) = some_strval {
-        if ["yes", "y", "on", "true"].contains(&val) {
-            true
-        } else if ["no", "n", "off", "false"].contains(&val) {
-            false
-        } else {
-            bug!(
-                "Unsupported value `{}` for option `{}` in environment variable {}",
-                option,
-                val,
-                RUSTC_COVERAGE_DEBUG_OPTIONS
-            )
-        }
-    } else {
-        true
-    }
-}
-
-fn counter_format_option_val(strval: &str) -> ExpressionFormat {
-    let mut counter_format = ExpressionFormat { id: false, block: false, operation: false };
-    let components = strval.splitn(3, '+');
-    for component in components {
-        match component {
-            "id" => counter_format.id = true,
-            "block" => counter_format.block = true,
-            "operation" => counter_format.operation = true,
-            _ => bug!(
-                "Unsupported counter_format choice `{}` in environment variable {}",
-                component,
-                RUSTC_COVERAGE_DEBUG_OPTIONS
-            ),
-        }
-    }
-    counter_format
-}
-
-#[derive(Debug, Clone)]
-struct ExpressionFormat {
-    id: bool,
-    block: bool,
-    operation: bool,
-}
-
-impl Default for ExpressionFormat {
-    fn default() -> Self {
-        Self { id: false, block: true, operation: true }
-    }
-}
-
-/// If enabled, this struct maintains a map from `BcbCounter` IDs (as `Operand`) to
-/// the `BcbCounter` data and optional label (normally, the counter's associated
-/// `BasicCoverageBlock` format string, if any).
-///
-/// Use `format_counter` to convert one of these `BcbCounter` counters to a debug output string,
-/// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump
-/// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment
-/// variable.
-///
-/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be
-/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`.
-pub(super) struct DebugCounters {
-    state: Option<DebugCountersState>,
-}
-
-#[derive(Default)]
-struct DebugCountersState {
-    counters: FxHashMap<Operand, DebugCounter>,
-}
-
-impl DebugCounters {
-    pub fn new() -> Self {
-        Self { state: None }
-    }
-
-    pub fn enable(&mut self) {
-        debug_assert!(!self.is_enabled());
-        self.state = Some(DebugCountersState::default());
-    }
-
-    pub fn is_enabled(&self) -> bool {
-        self.state.is_some()
-    }
-
-    pub fn add_counter(&mut self, counter_kind: &BcbCounter, some_block_label: Option<String>) {
-        let Some(state) = &mut self.state else { return };
-
-        let id = counter_kind.as_operand();
-        state
-            .counters
-            .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
-            .expect("attempt to add the same counter_kind to DebugCounters more than once");
-    }
-
-    pub fn some_block_label(&self, operand: Operand) -> Option<&String> {
-        let Some(state) = &self.state else { return None };
-
-        state.counters.get(&operand)?.some_block_label.as_ref()
-    }
-
-    pub fn format_counter(&self, counter_kind: &BcbCounter) -> String {
-        match *counter_kind {
-            BcbCounter::Counter { .. } => {
-                format!("Counter({})", self.format_counter_kind(counter_kind))
-            }
-            BcbCounter::Expression { .. } => {
-                format!("Expression({})", self.format_counter_kind(counter_kind))
-            }
-        }
-    }
-
-    fn format_counter_kind(&self, counter_kind: &BcbCounter) -> String {
-        let counter_format = &debug_options().counter_format;
-        if let BcbCounter::Expression { id, lhs, op, rhs } = *counter_kind {
-            if counter_format.operation {
-                return format!(
-                    "{}{} {} {}",
-                    if counter_format.id || !self.is_enabled() {
-                        format!("#{} = ", id.index())
-                    } else {
-                        String::new()
-                    },
-                    self.format_operand(lhs),
-                    match op {
-                        Op::Add => "+",
-                        Op::Subtract => "-",
-                    },
-                    self.format_operand(rhs),
-                );
-            }
-        }
-
-        let id = counter_kind.as_operand();
-        if let Some(state) = &self.state && (counter_format.block || !counter_format.id) {
-            if let Some(DebugCounter { some_block_label: Some(block_label), .. }) =
-                state.counters.get(&id)
-            {
-                return if counter_format.id {
-                    format!("{}#{:?}", block_label, id)
-                } else {
-                    block_label.to_string()
-                };
-            }
-        }
-        format!("#{:?}", id)
-    }
-
-    fn format_operand(&self, operand: Operand) -> String {
-        if matches!(operand, Operand::Zero) {
-            return String::from("0");
-        }
-        if let Some(state) = &self.state {
-            if let Some(DebugCounter { counter_kind, some_block_label }) =
-                state.counters.get(&operand)
-            {
-                if let BcbCounter::Expression { .. } = counter_kind {
-                    if let Some(label) = some_block_label && debug_options().counter_format.block {
-                        return format!(
-                            "{}:({})",
-                            label,
-                            self.format_counter_kind(counter_kind)
-                        );
-                    }
-                    return format!("({})", self.format_counter_kind(counter_kind));
-                }
-                return self.format_counter_kind(counter_kind);
-            }
-        }
-        format!("#{:?}", operand)
-    }
-}
-
-/// A non-public support class to `DebugCounters`.
-#[derive(Debug)]
-struct DebugCounter {
-    counter_kind: BcbCounter,
-    some_block_label: Option<String>,
-}
-
-impl DebugCounter {
-    fn new(counter_kind: BcbCounter, some_block_label: Option<String>) -> Self {
-        Self { counter_kind, some_block_label }
-    }
-}
-
-/// If enabled, this data structure captures additional debugging information used when generating
-/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes.
-pub(super) struct GraphvizData {
-    state: Option<GraphvizDataState>,
-}
-
-#[derive(Default)]
-struct GraphvizDataState {
-    bcb_to_coverage_spans_with_counters:
-        FxHashMap<BasicCoverageBlock, Vec<(CoverageSpan, BcbCounter)>>,
-    bcb_to_dependency_counters: FxHashMap<BasicCoverageBlock, Vec<BcbCounter>>,
-    edge_to_counter: FxHashMap<(BasicCoverageBlock, BasicBlock), BcbCounter>,
-}
-
-impl GraphvizData {
-    pub fn new() -> Self {
-        Self { state: None }
-    }
-
-    pub fn enable(&mut self) {
-        debug_assert!(!self.is_enabled());
-        self.state = Some(GraphvizDataState::default());
-    }
-
-    pub fn is_enabled(&self) -> bool {
-        self.state.is_some()
-    }
-
-    pub fn add_bcb_coverage_span_with_counter(
-        &mut self,
-        bcb: BasicCoverageBlock,
-        coverage_span: &CoverageSpan,
-        counter_kind: &BcbCounter,
-    ) {
-        let Some(state) = &mut self.state else { return };
-
-        state
-            .bcb_to_coverage_spans_with_counters
-            .entry(bcb)
-            .or_insert_with(Vec::new)
-            .push((coverage_span.clone(), counter_kind.clone()));
-    }
-
-    pub fn get_bcb_coverage_spans_with_counters(
-        &self,
-        bcb: BasicCoverageBlock,
-    ) -> Option<&[(CoverageSpan, BcbCounter)]> {
-        let Some(state) = &self.state else { return None };
-
-        state.bcb_to_coverage_spans_with_counters.get(&bcb).map(Deref::deref)
-    }
-
-    pub fn add_bcb_dependency_counter(
-        &mut self,
-        bcb: BasicCoverageBlock,
-        counter_kind: &BcbCounter,
-    ) {
-        let Some(state) = &mut self.state else { return };
-
-        state
-            .bcb_to_dependency_counters
-            .entry(bcb)
-            .or_insert_with(Vec::new)
-            .push(counter_kind.clone());
-    }
-
-    pub fn get_bcb_dependency_counters(&self, bcb: BasicCoverageBlock) -> Option<&[BcbCounter]> {
-        let Some(state) = &self.state else { return None };
-
-        state.bcb_to_dependency_counters.get(&bcb).map(Deref::deref)
-    }
-
-    pub fn set_edge_counter(
-        &mut self,
-        from_bcb: BasicCoverageBlock,
-        to_bb: BasicBlock,
-        counter_kind: &BcbCounter,
-    ) {
-        let Some(state) = &mut self.state else { return };
-
-        state
-            .edge_to_counter
-            .try_insert((from_bcb, to_bb), counter_kind.clone())
-            .expect("invalid attempt to insert more than one edge counter for the same edge");
-    }
-
-    pub fn get_edge_counter(
-        &self,
-        from_bcb: BasicCoverageBlock,
-        to_bb: BasicBlock,
-    ) -> Option<&BcbCounter> {
-        let Some(state) = &self.state else { return None };
-
-        state.edge_to_counter.get(&(from_bcb, to_bb))
-    }
-}
-
-/// If enabled, this struct captures additional data used to track whether expressions were used,
-/// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are
-/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs
-/// and/or a `CoverageGraph` graphviz output).
-pub(super) struct UsedExpressions {
-    state: Option<UsedExpressionsState>,
-}
-
-#[derive(Default)]
-struct UsedExpressionsState {
-    used_expression_operands: FxHashSet<Operand>,
-    unused_expressions: Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)>,
-}
-
-impl UsedExpressions {
-    pub fn new() -> Self {
-        Self { state: None }
-    }
-
-    pub fn enable(&mut self) {
-        debug_assert!(!self.is_enabled());
-        self.state = Some(UsedExpressionsState::default())
-    }
-
-    pub fn is_enabled(&self) -> bool {
-        self.state.is_some()
-    }
-
-    pub fn add_expression_operands(&mut self, expression: &BcbCounter) {
-        let Some(state) = &mut self.state else { return };
-
-        if let BcbCounter::Expression { lhs, rhs, .. } = *expression {
-            state.used_expression_operands.insert(lhs);
-            state.used_expression_operands.insert(rhs);
-        }
-    }
-
-    pub fn expression_is_used(&self, expression: &BcbCounter) -> bool {
-        let Some(state) = &self.state else { return false };
-
-        state.used_expression_operands.contains(&expression.as_operand())
-    }
-
-    pub fn add_unused_expression_if_not_found(
-        &mut self,
-        expression: &BcbCounter,
-        edge_from_bcb: Option<BasicCoverageBlock>,
-        target_bcb: BasicCoverageBlock,
-    ) {
-        let Some(state) = &mut self.state else { return };
-
-        if !state.used_expression_operands.contains(&expression.as_operand()) {
-            state.unused_expressions.push((expression.clone(), edge_from_bcb, target_bcb));
-        }
-    }
-
-    /// Return the list of unused counters (if any) as a tuple with the counter (`BcbCounter`),
-    /// optional `from_bcb` (if it was an edge counter), and `target_bcb`.
-    pub fn get_unused_expressions(
-        &self,
-    ) -> Vec<(BcbCounter, Option<BasicCoverageBlock>, BasicCoverageBlock)> {
-        let Some(state) = &self.state else { return Vec::new() };
-
-        state.unused_expressions.clone()
-    }
-
-    /// If enabled, validate that every BCB or edge counter not directly associated with a coverage
-    /// span is at least indirectly associated (it is a dependency of a BCB counter that _is_
-    /// associated with a coverage span).
-    pub fn validate(
-        &mut self,
-        bcb_counters_without_direct_coverage_spans: &[(
-            Option<BasicCoverageBlock>,
-            BasicCoverageBlock,
-            BcbCounter,
-        )],
-    ) {
-        if !self.is_enabled() {
-            return;
-        }
-
-        let mut not_validated = bcb_counters_without_direct_coverage_spans
-            .iter()
-            .map(|(_, _, counter_kind)| counter_kind)
-            .collect::<Vec<_>>();
-        let mut validating_count = 0;
-        while not_validated.len() != validating_count {
-            let to_validate = not_validated.split_off(0);
-            validating_count = to_validate.len();
-            for counter_kind in to_validate {
-                if self.expression_is_used(counter_kind) {
-                    self.add_expression_operands(counter_kind);
-                } else {
-                    not_validated.push(counter_kind);
-                }
-            }
-        }
-    }
-
-    pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) {
-        let Some(state) = &self.state else { return };
-
-        for (counter_kind, edge_from_bcb, target_bcb) in &state.unused_expressions {
-            let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() {
-                format!(
-                    "non-coverage edge counter found without a dependent expression, in \
-                        {:?}->{:?}; counter={}",
-                    from_bcb,
-                    target_bcb,
-                    debug_counters.format_counter(&counter_kind),
-                )
-            } else {
-                format!(
-                    "non-coverage counter found without a dependent expression, in {:?}; \
-                        counter={}",
-                    target_bcb,
-                    debug_counters.format_counter(&counter_kind),
-                )
-            };
-
-            if debug_options().allow_unused_expressions {
-                debug!("WARNING: {}", unused_counter_message);
-            } else {
-                bug!("{}", unused_counter_message);
-            }
-        }
-    }
-}
-
-/// Generates the MIR pass `CoverageSpan`-specific spanview dump file.
-pub(super) fn dump_coverage_spanview<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    mir_body: &mir::Body<'tcx>,
-    basic_coverage_blocks: &CoverageGraph,
-    pass_name: &str,
-    body_span: Span,
-    coverage_spans: &[CoverageSpan],
-) {
-    let mir_source = mir_body.source;
-    let def_id = mir_source.def_id();
-
-    let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans);
-    let mut file = create_dump_file(tcx, "html", false, pass_name, &0i32, mir_body)
-        .expect("Unexpected error creating MIR spanview HTML file");
-    let crate_name = tcx.crate_name(def_id.krate);
-    let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();
-    let title = format!("{crate_name}.{item_name} - Coverage Spans");
-    spanview::write_document(tcx, body_span, span_viewables, &title, &mut file)
-        .expect("Unexpected IO error dumping coverage spans as HTML");
-}
-
-/// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s.
-fn span_viewables<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    mir_body: &mir::Body<'tcx>,
-    basic_coverage_blocks: &CoverageGraph,
-    coverage_spans: &[CoverageSpan],
-) -> Vec<SpanViewable> {
-    let mut span_viewables = Vec::new();
-    for coverage_span in coverage_spans {
-        let tooltip = coverage_span.format_coverage_statements(tcx, mir_body);
-        let CoverageSpan { span, bcb, .. } = coverage_span;
-        let bcb_data = &basic_coverage_blocks[*bcb];
-        let id = bcb_data.id();
-        let leader_bb = bcb_data.leader_bb();
-        span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip });
-    }
-    span_viewables
-}
-
-/// Generates the MIR pass coverage-specific graphviz dump file.
-pub(super) fn dump_coverage_graphviz<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    mir_body: &mir::Body<'tcx>,
-    pass_name: &str,
-    basic_coverage_blocks: &CoverageGraph,
-    coverage_counters: &CoverageCounters,
-    graphviz_data: &GraphvizData,
-    intermediate_expressions: &[BcbCounter],
-    debug_used_expressions: &UsedExpressions,
-) {
-    let debug_counters = &coverage_counters.debug_counters;
-
-    let mir_source = mir_body.source;
-    let def_id = mir_source.def_id();
-    let node_content = |bcb| {
-        bcb_to_string_sections(
-            tcx,
-            mir_body,
-            coverage_counters,
-            bcb,
-            &basic_coverage_blocks[bcb],
-            graphviz_data.get_bcb_coverage_spans_with_counters(bcb),
-            graphviz_data.get_bcb_dependency_counters(bcb),
-            // intermediate_expressions are injected into the mir::START_BLOCK, so
-            // include them in the first BCB.
-            if bcb.index() == 0 { Some(&intermediate_expressions) } else { None },
-        )
-    };
-    let edge_labels = |from_bcb| {
-        let from_bcb_data = &basic_coverage_blocks[from_bcb];
-        let from_terminator = from_bcb_data.terminator(mir_body);
-        let mut edge_labels = from_terminator.kind.fmt_successor_labels();
-        edge_labels.retain(|label| label != "unreachable");
-        let edge_counters = from_terminator
-            .successors()
-            .map(|successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb));
-        iter::zip(&edge_labels, edge_counters)
-            .map(|(label, some_counter)| {
-                if let Some(counter) = some_counter {
-                    format!("{}\n{}", label, debug_counters.format_counter(counter))
-                } else {
-                    label.to_string()
-                }
-            })
-            .collect::<Vec<_>>()
-    };
-    let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index());
-    let mut graphviz_writer =
-        GraphvizWriter::new(basic_coverage_blocks, &graphviz_name, node_content, edge_labels);
-    let unused_expressions = debug_used_expressions.get_unused_expressions();
-    if unused_expressions.len() > 0 {
-        graphviz_writer.set_graph_label(&format!(
-            "Unused expressions:\n  {}",
-            unused_expressions
-                .as_slice()
-                .iter()
-                .map(|(counter_kind, edge_from_bcb, target_bcb)| {
-                    if let Some(from_bcb) = edge_from_bcb.as_ref() {
-                        format!(
-                            "{:?}->{:?}: {}",
-                            from_bcb,
-                            target_bcb,
-                            debug_counters.format_counter(&counter_kind),
-                        )
-                    } else {
-                        format!(
-                            "{:?}: {}",
-                            target_bcb,
-                            debug_counters.format_counter(&counter_kind),
-                        )
-                    }
-                })
-                .join("\n  ")
-        ));
-    }
-    let mut file = create_dump_file(tcx, "dot", false, pass_name, &0i32, mir_body)
-        .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file");
-    graphviz_writer
-        .write_graphviz(tcx, &mut file)
-        .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file");
-}
-
-fn bcb_to_string_sections<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    mir_body: &mir::Body<'tcx>,
-    coverage_counters: &CoverageCounters,
-    bcb: BasicCoverageBlock,
-    bcb_data: &BasicCoverageBlockData,
-    some_coverage_spans_with_counters: Option<&[(CoverageSpan, BcbCounter)]>,
-    some_dependency_counters: Option<&[BcbCounter]>,
-    some_intermediate_expressions: Option<&[BcbCounter]>,
-) -> Vec<String> {
-    let debug_counters = &coverage_counters.debug_counters;
-
-    let len = bcb_data.basic_blocks.len();
-    let mut sections = Vec::new();
-    if let Some(collect_intermediate_expressions) = some_intermediate_expressions {
-        sections.push(
-            collect_intermediate_expressions
-                .iter()
-                .map(|expression| {
-                    format!("Intermediate {}", debug_counters.format_counter(expression))
-                })
-                .join("\n"),
-        );
-    }
-    if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters {
-        sections.push(
-            coverage_spans_with_counters
-                .iter()
-                .map(|(covspan, counter)| {
-                    format!(
-                        "{} at {}",
-                        debug_counters.format_counter(counter),
-                        covspan.format(tcx, mir_body)
-                    )
-                })
-                .join("\n"),
-        );
-    }
-    if let Some(dependency_counters) = some_dependency_counters {
-        sections.push(format!(
-            "Non-coverage counters:\n  {}",
-            dependency_counters
-                .iter()
-                .map(|counter| debug_counters.format_counter(counter))
-                .join("  \n"),
-        ));
-    }
-    if let Some(counter_kind) = coverage_counters.bcb_counter(bcb) {
-        sections.push(format!("{counter_kind:?}"));
-    }
-    let non_term_blocks = bcb_data.basic_blocks[0..len - 1]
-        .iter()
-        .map(|&bb| format!("{:?}: {}", bb, mir_body[bb].terminator().kind.name()))
-        .collect::<Vec<_>>();
-    if non_term_blocks.len() > 0 {
-        sections.push(non_term_blocks.join("\n"));
-    }
-    sections.push(format!(
-        "{:?}: {}",
-        bcb_data.basic_blocks.last().unwrap(),
-        bcb_data.terminator(mir_body).kind.name(),
-    ));
-    sections
-}
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index b6b0463614d..ff2254d6941 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -1,4 +1,3 @@
-use itertools::Itertools;
 use rustc_data_structures::graph::dominators::{self, Dominators};
 use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode};
 use rustc_index::bit_set::BitSet;
@@ -8,8 +7,6 @@ use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, Terminator
 use std::cmp::Ordering;
 use std::ops::{Index, IndexMut};
 
-const ID_SEPARATOR: &str = ",";
-
 /// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
 /// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s.
 #[derive(Debug)]
@@ -324,10 +321,6 @@ impl BasicCoverageBlockData {
     pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> {
         &mir_body[self.last_bb()].terminator()
     }
-
-    pub fn id(&self) -> String {
-        format!("@{}", self.basic_blocks.iter().map(|bb| bb.index().to_string()).join(ID_SEPARATOR))
-    }
 }
 
 /// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`)
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index d0b28eb2f5d..c75d33eeb31 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -1,7 +1,6 @@
 pub mod query;
 
 mod counters;
-mod debug;
 mod graph;
 mod spans;
 
@@ -20,7 +19,6 @@ use rustc_index::IndexVec;
 use rustc_middle::hir;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::coverage::*;
-use rustc_middle::mir::dump_enabled;
 use rustc_middle::mir::{
     self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
     TerminatorKind,
@@ -94,13 +92,12 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
         }
 
         trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
-        Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
+        Instrumentor::new(tcx, mir_body).inject_counters();
         trace!("InstrumentCoverage done for {:?}", mir_source.def_id());
     }
 }
 
 struct Instrumentor<'a, 'tcx> {
-    pass_name: &'a str,
     tcx: TyCtxt<'tcx>,
     mir_body: &'a mut mir::Body<'tcx>,
     source_file: Lrc<SourceFile>,
@@ -112,7 +109,7 @@ struct Instrumentor<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
-    fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
+    fn new(tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
         let source_map = tcx.sess.source_map();
         let def_id = mir_body.source.def_id();
         let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, def_id);
@@ -141,7 +138,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         let coverage_counters = CoverageCounters::new(&basic_coverage_blocks);
 
         Self {
-            pass_name,
             tcx,
             mir_body,
             source_file,
@@ -154,28 +150,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
     }
 
     fn inject_counters(&'a mut self) {
-        let tcx = self.tcx;
-        let mir_source = self.mir_body.source;
-        let def_id = mir_source.def_id();
         let fn_sig_span = self.fn_sig_span;
         let body_span = self.body_span;
 
-        let mut graphviz_data = debug::GraphvizData::new();
-        let mut debug_used_expressions = debug::UsedExpressions::new();
-
-        let dump_mir = dump_enabled(tcx, self.pass_name, def_id);
-        let dump_graphviz = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_graphviz;
-        let dump_spanview = dump_mir && tcx.sess.opts.unstable_opts.dump_mir_spanview.is_some();
-
-        if dump_graphviz {
-            graphviz_data.enable();
-            self.coverage_counters.enable_debug();
-        }
-
-        if dump_graphviz || level_enabled!(tracing::Level::DEBUG) {
-            debug_used_expressions.enable();
-        }
-
         ////////////////////////////////////////////////////
         // Compute `CoverageSpan`s from the `CoverageGraph`.
         let coverage_spans = CoverageSpans::generate_coverage_spans(
@@ -185,17 +162,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
             &self.basic_coverage_blocks,
         );
 
-        if dump_spanview {
-            debug::dump_coverage_spanview(
-                tcx,
-                self.mir_body,
-                &self.basic_coverage_blocks,
-                self.pass_name,
-                body_span,
-                &coverage_spans,
-            );
-        }
-
         ////////////////////////////////////////////////////
         // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
         // every `CoverageSpan` has a `Counter` or `Expression` assigned to its `BasicCoverageBlock`
@@ -209,14 +175,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
             .make_bcb_counters(&mut self.basic_coverage_blocks, &coverage_spans);
 
         if let Ok(()) = result {
-            // If debugging, add any intermediate expressions (which are not associated with any
-            // BCB) to the `debug_used_expressions` map.
-            if debug_used_expressions.is_enabled() {
-                for intermediate_expression in &self.coverage_counters.intermediate_expressions {
-                    debug_used_expressions.add_expression_operands(intermediate_expression);
-                }
-            }
-
             ////////////////////////////////////////////////////
             // Remove the counter or edge counter from of each `CoverageSpan`s associated
             // `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR.
@@ -227,11 +185,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
             // These `CoverageSpan`-associated counters are removed from their associated
             // `BasicCoverageBlock`s so that the only remaining counters in the `CoverageGraph`
             // are indirect counters (to be injected next, without associated code regions).
-            self.inject_coverage_span_counters(
-                coverage_spans,
-                &mut graphviz_data,
-                &mut debug_used_expressions,
-            );
+            self.inject_coverage_span_counters(coverage_spans);
 
             ////////////////////////////////////////////////////
             // For any remaining `BasicCoverageBlock` counters (that were not associated with
@@ -239,37 +193,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
             // to ensure `BasicCoverageBlock` counters that other `Expression`s may depend on
             // are in fact counted, even though they don't directly contribute to counting
             // their own independent code region's coverage.
-            self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions);
+            self.inject_indirect_counters();
 
             // Intermediate expressions will be injected as the final step, after generating
             // debug output, if any.
             ////////////////////////////////////////////////////
         };
 
-        if graphviz_data.is_enabled() {
-            // Even if there was an error, a partial CoverageGraph can still generate a useful
-            // graphviz output.
-            debug::dump_coverage_graphviz(
-                tcx,
-                self.mir_body,
-                self.pass_name,
-                &self.basic_coverage_blocks,
-                &self.coverage_counters,
-                &graphviz_data,
-                &self.coverage_counters.intermediate_expressions,
-                &debug_used_expressions,
-            );
-        }
-
         if let Err(e) = result {
             bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e.message)
         };
 
-        // Depending on current `debug_options()`, `alert_on_unused_expressions()` could panic, so
-        // this check is performed as late as possible, to allow other debug output (logs and dump
-        // files), which might be helpful in analyzing unused expressions, to still be generated.
-        debug_used_expressions.alert_on_unused_expressions(&self.coverage_counters.debug_counters);
-
         ////////////////////////////////////////////////////
         // Finally, inject the intermediate expressions collected along the way.
         for intermediate_expression in &self.coverage_counters.intermediate_expressions {
@@ -285,15 +219,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
     /// `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s for a BCB that already has
     /// a `Counter` will inject an `Expression` instead, and compute its value by adding `ZERO` to
     /// the BCB `Counter` value.
-    ///
-    /// If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the
-    /// `used_expression_operands` map.
-    fn inject_coverage_span_counters(
-        &mut self,
-        coverage_spans: Vec<CoverageSpan>,
-        graphviz_data: &mut debug::GraphvizData,
-        debug_used_expressions: &mut debug::UsedExpressions,
-    ) {
+    fn inject_coverage_span_counters(&mut self, coverage_spans: Vec<CoverageSpan>) {
         let tcx = self.tcx;
         let source_map = tcx.sess.source_map();
         let body_span = self.body_span;
@@ -307,12 +233,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
                 self.coverage_counters.make_identity_counter(counter_operand)
             } else if let Some(counter_kind) = self.coverage_counters.take_bcb_counter(bcb) {
                 bcb_counters[bcb] = Some(counter_kind.as_operand());
-                debug_used_expressions.add_expression_operands(&counter_kind);
                 counter_kind
             } else {
                 bug!("Every BasicCoverageBlock should have a Counter or Expression");
             };
-            graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind);
 
             let code_region = make_code_region(source_map, file_name, span, body_span);
 
@@ -333,11 +257,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
     /// associated with a `CoverageSpan`, should only exist if the counter is an `Expression`
     /// dependency (one of the expression operands). Collect them, and inject the additional
     /// counters into the MIR, without a reportable coverage span.
-    fn inject_indirect_counters(
-        &mut self,
-        graphviz_data: &mut debug::GraphvizData,
-        debug_used_expressions: &mut debug::UsedExpressions,
-    ) {
+    fn inject_indirect_counters(&mut self) {
         let mut bcb_counters_without_direct_coverage_spans = Vec::new();
         for (target_bcb, counter_kind) in self.coverage_counters.drain_bcb_counters() {
             bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind));
@@ -352,19 +272,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
             ));
         }
 
-        // If debug is enabled, validate that every BCB or edge counter not directly associated
-        // with a coverage span is at least indirectly associated (it is a dependency of a BCB
-        // counter that _is_ associated with a coverage span).
-        debug_used_expressions.validate(&bcb_counters_without_direct_coverage_spans);
-
         for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans
         {
-            debug_used_expressions.add_unused_expression_if_not_found(
-                &counter_kind,
-                edge_from_bcb,
-                target_bcb,
-            );
-
             match counter_kind {
                 BcbCounter::Counter { .. } => {
                     let inject_to_bb = if let Some(from_bcb) = edge_from_bcb {
@@ -375,26 +284,17 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
                         let to_bb = self.bcb_leader_bb(target_bcb);
 
                         let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb);
-                        graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind);
                         debug!(
                             "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \
-                            BasicBlock {:?}, for unclaimed edge counter {}",
-                            edge_from_bcb,
-                            from_bb,
-                            target_bcb,
-                            to_bb,
-                            new_bb,
-                            self.format_counter(&counter_kind),
+                            BasicBlock {:?}, for unclaimed edge counter {:?}",
+                            edge_from_bcb, from_bb, target_bcb, to_bb, new_bb, counter_kind,
                         );
                         new_bb
                     } else {
                         let target_bb = self.bcb_last_bb(target_bcb);
-                        graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind);
                         debug!(
-                            "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}",
-                            target_bcb,
-                            target_bb,
-                            self.format_counter(&counter_kind),
+                            "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {:?}",
+                            target_bcb, target_bb, counter_kind,
                         );
                         target_bb
                     };
@@ -429,11 +329,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         &self.basic_coverage_blocks[bcb]
     }
 
-    #[inline]
-    fn format_counter(&self, counter_kind: &BcbCounter) -> String {
-        self.coverage_counters.debug_counters.format_counter(counter_kind)
-    }
-
     fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind {
         match *counter_kind {
             BcbCounter::Counter { id } => {
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 32e8ca25d31..5b24fa10bea 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,13 +1,10 @@
 use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB};
 
-use itertools::Itertools;
 use rustc_data_structures::graph::WithNumNodes;
-use rustc_middle::mir::spanview::source_range_no_file;
 use rustc_middle::mir::{
     self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
     TerminatorKind,
 };
-use rustc_middle::ty::TyCtxt;
 use rustc_span::source_map::original_sp;
 use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
 
@@ -20,31 +17,6 @@ pub(super) enum CoverageStatement {
 }
 
 impl CoverageStatement {
-    pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
-        match *self {
-            Self::Statement(bb, span, stmt_index) => {
-                let stmt = &mir_body[bb].statements[stmt_index];
-                format!(
-                    "{}: @{}[{}]: {:?}",
-                    source_range_no_file(tcx, span),
-                    bb.index(),
-                    stmt_index,
-                    stmt
-                )
-            }
-            Self::Terminator(bb, span) => {
-                let term = mir_body[bb].terminator();
-                format!(
-                    "{}: @{}.{}: {:?}",
-                    source_range_no_file(tcx, span),
-                    bb.index(),
-                    term.kind.name(),
-                    term.kind
-                )
-            }
-        }
-    }
-
     pub fn span(&self) -> Span {
         match self {
             Self::Statement(_, span, _) | Self::Terminator(_, span) => *span,
@@ -150,27 +122,6 @@ impl CoverageSpan {
         self.bcb == other.bcb
     }
 
-    pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
-        format!(
-            "{}\n    {}",
-            source_range_no_file(tcx, self.span),
-            self.format_coverage_statements(tcx, mir_body).replace('\n', "\n    "),
-        )
-    }
-
-    pub fn format_coverage_statements<'tcx>(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        mir_body: &mir::Body<'tcx>,
-    ) -> String {
-        let mut sorted_coverage_statements = self.coverage_statements.clone();
-        sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt {
-            CoverageStatement::Statement(bb, _, index) => (bb, index),
-            CoverageStatement::Terminator(bb, _) => (bb, usize::MAX),
-        });
-        sorted_coverage_statements.iter().map(|covstmt| covstmt.format(tcx, mir_body)).join("\n")
-    }
-
     /// If the span is part of a macro, returns the macro name symbol.
     pub fn current_macro(&self) -> Option<Symbol> {
         self.current_macro_or_none
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index c7c5f17dfec..cf827c98894 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -6,7 +6,7 @@ use rustc_const_eval::const_eval::CheckAlignment;
 use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
-use rustc_middle::mir::interpret::{AllocId, ConstAllocation, ConstValue, InterpResult, Scalar};
+use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar};
 use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::TyAndLayout;
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 8afbe418502..fc49c9ba348 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -153,7 +153,7 @@ impl EnumSizeOpt {
                         span,
                         user_ty: None,
                         literal: ConstantKind::Val(
-                            interpret::ConstValue::Indirect { alloc_id, offset: Size::ZERO },
+                            ConstValue::Indirect { alloc_id, offset: Size::ZERO },
                             tmp_ty,
                         ),
                     };
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index c13bafa9fbb..dcc4cd85cda 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -1,7 +1,6 @@
 //! Removes operations on ZST places, and convert ZST operands to constants.
 
 use crate::MirPass;
-use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::mir::visit::*;
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 92abd0c3b6e..c8b72107756 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -170,8 +170,7 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::lang_items::LangItem;
-use rustc_middle::mir::interpret::{AllocId, ConstValue};
-use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar};
+use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar};
 use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
 use rustc_middle::mir::visit::Visitor as MirVisitor;
 use rustc_middle::mir::{self, Local, Location};
@@ -1442,13 +1441,15 @@ fn collect_used_items<'tcx>(
 #[instrument(skip(tcx, output), level = "debug")]
 fn collect_const_value<'tcx>(
     tcx: TyCtxt<'tcx>,
-    value: ConstValue<'tcx>,
+    value: mir::ConstValue<'tcx>,
     output: &mut MonoItems<'tcx>,
 ) {
     match value {
-        ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_alloc(tcx, ptr.provenance, output),
-        ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
-        ConstValue::Slice { data, start: _, end: _ } => {
+        mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => {
+            collect_alloc(tcx, ptr.provenance, output)
+        }
+        mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
+        mir::ConstValue::Slice { data, meta: _ } => {
             for &id in data.inner().provenance().ptrs().values() {
                 collect_alloc(tcx, id, output);
             }
diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs
index 213e5c8ba68..07eb10cba02 100644
--- a/compiler/rustc_query_system/src/dep_graph/serialized.rs
+++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs
@@ -404,7 +404,7 @@ struct NodeInfo<K: DepKind> {
 impl<K: DepKind> Encodable<FileEncoder> for NodeInfo<K> {
     fn encode(&self, e: &mut FileEncoder) {
         let header = SerializedNodeHeader::new(self);
-        e.emit_raw_bytes(&header.bytes);
+        e.write_array(header.bytes);
 
         if header.len().is_none() {
             e.emit_usize(self.edges.len());
@@ -412,8 +412,10 @@ impl<K: DepKind> Encodable<FileEncoder> for NodeInfo<K> {
 
         let bytes_per_index = header.bytes_per_index();
         for node_index in self.edges.iter() {
-            let bytes = node_index.as_u32().to_le_bytes();
-            e.emit_raw_bytes(&bytes[..bytes_per_index]);
+            e.write_with(|dest| {
+                *dest = node_index.as_u32().to_le_bytes();
+                bytes_per_index
+            });
         }
     }
 }
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 820ac9ef61b..d271519a8a3 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -991,9 +991,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     if !is_prelude
                     && let Some(max_vis) = max_vis.get()
                     && !max_vis.is_at_least(import.expect_vis(), self.tcx)
-                {
-                    self.lint_buffer.buffer_lint(UNUSED_IMPORTS, id, import.span, fluent::resolve_glob_import_doesnt_reexport);
-                }
+                    {
+                        self.lint_buffer.buffer_lint(UNUSED_IMPORTS, id, import.span, fluent::resolve_glob_import_doesnt_reexport);
+                    }
                     return None;
                 }
                 _ => unreachable!(),
diff --git a/compiler/rustc_serialize/src/leb128.rs b/compiler/rustc_serialize/src/leb128.rs
index e568b9e6786..ca661bac78c 100644
--- a/compiler/rustc_serialize/src/leb128.rs
+++ b/compiler/rustc_serialize/src/leb128.rs
@@ -15,23 +15,20 @@ pub const fn largest_max_leb128_len() -> usize {
 macro_rules! impl_write_unsigned_leb128 {
     ($fn_name:ident, $int_ty:ty) => {
         #[inline]
-        pub fn $fn_name(
-            out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len::<$int_ty>()],
-            mut value: $int_ty,
-        ) -> &[u8] {
+        pub fn $fn_name(out: &mut [u8; max_leb128_len::<$int_ty>()], mut value: $int_ty) -> usize {
             let mut i = 0;
 
             loop {
                 if value < 0x80 {
                     unsafe {
-                        *out.get_unchecked_mut(i).as_mut_ptr() = value as u8;
+                        *out.get_unchecked_mut(i) = value as u8;
                     }
 
                     i += 1;
                     break;
                 } else {
                     unsafe {
-                        *out.get_unchecked_mut(i).as_mut_ptr() = ((value & 0x7f) | 0x80) as u8;
+                        *out.get_unchecked_mut(i) = ((value & 0x7f) | 0x80) as u8;
                     }
 
                     value >>= 7;
@@ -39,7 +36,7 @@ macro_rules! impl_write_unsigned_leb128 {
                 }
             }
 
-            unsafe { ::std::mem::MaybeUninit::slice_assume_init_ref(&out.get_unchecked(..i)) }
+            i
         }
     };
 }
@@ -87,10 +84,7 @@ impl_read_unsigned_leb128!(read_usize_leb128, usize);
 macro_rules! impl_write_signed_leb128 {
     ($fn_name:ident, $int_ty:ty) => {
         #[inline]
-        pub fn $fn_name(
-            out: &mut [::std::mem::MaybeUninit<u8>; max_leb128_len::<$int_ty>()],
-            mut value: $int_ty,
-        ) -> &[u8] {
+        pub fn $fn_name(out: &mut [u8; max_leb128_len::<$int_ty>()], mut value: $int_ty) -> usize {
             let mut i = 0;
 
             loop {
@@ -104,7 +98,7 @@ macro_rules! impl_write_signed_leb128 {
                 }
 
                 unsafe {
-                    *out.get_unchecked_mut(i).as_mut_ptr() = byte;
+                    *out.get_unchecked_mut(i) = byte;
                 }
 
                 i += 1;
@@ -114,7 +108,7 @@ macro_rules! impl_write_signed_leb128 {
                 }
             }
 
-            unsafe { ::std::mem::MaybeUninit::slice_assume_init_ref(&out.get_unchecked(..i)) }
+            i
         }
     };
 }
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index ce8503918b4..dd40b3cf028 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -17,6 +17,9 @@ Core encoding and decoding interfaces.
 #![feature(new_uninit)]
 #![feature(allocator_api)]
 #![feature(ptr_sub_ptr)]
+#![feature(slice_first_last_chunk)]
+#![feature(inline_const)]
+#![feature(const_option)]
 #![cfg_attr(test, feature(test))]
 #![allow(rustc::internal)]
 #![deny(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index f1b7e8d9ae0..44855ae629c 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -3,10 +3,8 @@ use crate::serialize::{Decodable, Decoder, Encodable, Encoder};
 use std::fs::File;
 use std::io::{self, Write};
 use std::marker::PhantomData;
-use std::mem::MaybeUninit;
 use std::ops::Range;
 use std::path::Path;
-use std::ptr;
 
 // -----------------------------------------------------------------------------
 // Encoder
@@ -24,10 +22,12 @@ const BUF_SIZE: usize = 8192;
 /// size of the buffer, rather than the full length of the encoded data, and
 /// because it doesn't need to reallocate memory along the way.
 pub struct FileEncoder {
-    /// The input buffer. For adequate performance, we need more control over
-    /// buffering than `BufWriter` offers. If `BufWriter` ever offers a raw
-    /// buffer access API, we can use it, and remove `buf` and `buffered`.
-    buf: Box<[MaybeUninit<u8>]>,
+    // The input buffer. For adequate performance, we need to be able to write
+    // directly to the unwritten region of the buffer, without calling copy_from_slice.
+    // Note that our buffer is always initialized so that we can do that direct access
+    // without unsafe code. Users of this type write many more than BUF_SIZE bytes, so the
+    // initialization is approximately free.
+    buf: Box<[u8; BUF_SIZE]>,
     buffered: usize,
     flushed: usize,
     file: File,
@@ -38,15 +38,11 @@ pub struct FileEncoder {
 
 impl FileEncoder {
     pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
-        // Create the file for reading and writing, because some encoders do both
-        // (e.g. the metadata encoder when -Zmeta-stats is enabled)
-        let file = File::options().read(true).write(true).create(true).truncate(true).open(path)?;
-
         Ok(FileEncoder {
-            buf: Box::new_uninit_slice(BUF_SIZE),
+            buf: vec![0u8; BUF_SIZE].into_boxed_slice().try_into().unwrap(),
             buffered: 0,
             flushed: 0,
-            file,
+            file: File::create(path)?,
             res: Ok(()),
         })
     }
@@ -54,94 +50,19 @@ impl FileEncoder {
     #[inline]
     pub fn position(&self) -> usize {
         // Tracking position this way instead of having a `self.position` field
-        // means that we don't have to update the position on every write call.
+        // means that we only need to update `self.buffered` on a write call,
+        // as opposed to updating `self.position` and `self.buffered`.
         self.flushed + self.buffered
     }
 
+    #[cold]
+    #[inline(never)]
     pub fn flush(&mut self) {
-        // This is basically a copy of `BufWriter::flush`. If `BufWriter` ever
-        // offers a raw buffer access API, we can use it, and remove this.
-
-        /// Helper struct to ensure the buffer is updated after all the writes
-        /// are complete. It tracks the number of written bytes and drains them
-        /// all from the front of the buffer when dropped.
-        struct BufGuard<'a> {
-            buffer: &'a mut [u8],
-            encoder_buffered: &'a mut usize,
-            encoder_flushed: &'a mut usize,
-            flushed: usize,
-        }
-
-        impl<'a> BufGuard<'a> {
-            fn new(
-                buffer: &'a mut [u8],
-                encoder_buffered: &'a mut usize,
-                encoder_flushed: &'a mut usize,
-            ) -> Self {
-                assert_eq!(buffer.len(), *encoder_buffered);
-                Self { buffer, encoder_buffered, encoder_flushed, flushed: 0 }
-            }
-
-            /// The unwritten part of the buffer
-            fn remaining(&self) -> &[u8] {
-                &self.buffer[self.flushed..]
-            }
-
-            /// Flag some bytes as removed from the front of the buffer
-            fn consume(&mut self, amt: usize) {
-                self.flushed += amt;
-            }
-
-            /// true if all of the bytes have been written
-            fn done(&self) -> bool {
-                self.flushed >= *self.encoder_buffered
-            }
-        }
-
-        impl Drop for BufGuard<'_> {
-            fn drop(&mut self) {
-                if self.flushed > 0 {
-                    if self.done() {
-                        *self.encoder_flushed += *self.encoder_buffered;
-                        *self.encoder_buffered = 0;
-                    } else {
-                        self.buffer.copy_within(self.flushed.., 0);
-                        *self.encoder_flushed += self.flushed;
-                        *self.encoder_buffered -= self.flushed;
-                    }
-                }
-            }
-        }
-
-        // If we've already had an error, do nothing. It'll get reported after
-        // `finish` is called.
-        if self.res.is_err() {
-            return;
-        }
-
-        let mut guard = BufGuard::new(
-            unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[..self.buffered]) },
-            &mut self.buffered,
-            &mut self.flushed,
-        );
-
-        while !guard.done() {
-            match self.file.write(guard.remaining()) {
-                Ok(0) => {
-                    self.res = Err(io::Error::new(
-                        io::ErrorKind::WriteZero,
-                        "failed to write the buffered data",
-                    ));
-                    return;
-                }
-                Ok(n) => guard.consume(n),
-                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
-                Err(e) => {
-                    self.res = Err(e);
-                    return;
-                }
-            }
+        if self.res.is_ok() {
+            self.res = self.file.write_all(&self.buf[..self.buffered]);
         }
+        self.flushed += self.buffered;
+        self.buffered = 0;
     }
 
     pub fn file(&self) -> &File {
@@ -149,91 +70,89 @@ impl FileEncoder {
     }
 
     #[inline]
-    fn write_one(&mut self, value: u8) {
-        let mut buffered = self.buffered;
-
-        if std::intrinsics::unlikely(buffered + 1 > BUF_SIZE) {
-            self.flush();
-            buffered = 0;
-        }
+    fn buffer_empty(&mut self) -> &mut [u8] {
+        // SAFETY: self.buffered is inbounds as an invariant of the type
+        unsafe { self.buf.get_unchecked_mut(self.buffered..) }
+    }
 
-        // SAFETY: The above check and `flush` ensures that there is enough
-        // room to write the input to the buffer.
-        unsafe {
-            *MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered) = value;
+    #[cold]
+    #[inline(never)]
+    fn write_all_cold_path(&mut self, buf: &[u8]) {
+        self.flush();
+        if let Some(dest) = self.buf.get_mut(..buf.len()) {
+            dest.copy_from_slice(buf);
+            self.buffered += buf.len();
+        } else {
+            if self.res.is_ok() {
+                self.res = self.file.write_all(buf);
+            }
+            self.flushed += buf.len();
         }
-
-        self.buffered = buffered + 1;
     }
 
     #[inline]
     fn write_all(&mut self, buf: &[u8]) {
-        let buf_len = buf.len();
-
-        if std::intrinsics::likely(buf_len <= BUF_SIZE) {
-            let mut buffered = self.buffered;
-
-            if std::intrinsics::unlikely(buffered + buf_len > BUF_SIZE) {
-                self.flush();
-                buffered = 0;
-            }
-
-            // SAFETY: The above check and `flush` ensures that there is enough
-            // room to write the input to the buffer.
-            unsafe {
-                let src = buf.as_ptr();
-                let dst = MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered);
-                ptr::copy_nonoverlapping(src, dst, buf_len);
-            }
-
-            self.buffered = buffered + buf_len;
+        if let Some(dest) = self.buffer_empty().get_mut(..buf.len()) {
+            dest.copy_from_slice(buf);
+            self.buffered += buf.len();
         } else {
-            self.write_all_unbuffered(buf);
+            self.write_all_cold_path(buf);
         }
     }
 
-    fn write_all_unbuffered(&mut self, mut buf: &[u8]) {
-        // If we've already had an error, do nothing. It'll get reported after
-        // `finish` is called.
-        if self.res.is_err() {
-            return;
-        }
-
-        if self.buffered > 0 {
+    /// Write up to `N` bytes to this encoder.
+    ///
+    /// This function can be used to avoid the overhead of calling memcpy for writes that
+    /// have runtime-variable length, but are small and have a small fixed upper bound.
+    ///
+    /// This can be used to do in-place encoding as is done for leb128 (without this function
+    /// we would need to write to a temporary buffer then memcpy into the encoder), and it can
+    /// also be used to implement the varint scheme we use for rmeta and dep graph encoding,
+    /// where we only want to encode the first few bytes of an integer. Copying in the whole
+    /// integer then only advancing the encoder state for the few bytes we care about is more
+    /// efficient than calling [`FileEncoder::write_all`], because variable-size copies are
+    /// always lowered to `memcpy`, which has overhead and contains a lot of logic we can bypass
+    /// with this function. Note that common architectures support fixed-size writes up to 8 bytes
+    /// with one instruction, so while this does in some sense do wasted work, we come out ahead.
+    #[inline]
+    pub fn write_with<const N: usize>(&mut self, visitor: impl FnOnce(&mut [u8; N]) -> usize) {
+        let flush_threshold = const { BUF_SIZE.checked_sub(N).unwrap() };
+        if std::intrinsics::unlikely(self.buffered > flush_threshold) {
             self.flush();
         }
-
-        // This is basically a copy of `Write::write_all` but also updates our
-        // `self.flushed`. It's necessary because `Write::write_all` does not
-        // return the number of bytes written when an error is encountered, and
-        // without that, we cannot accurately update `self.flushed` on error.
-        while !buf.is_empty() {
-            match self.file.write(buf) {
-                Ok(0) => {
-                    self.res = Err(io::Error::new(
-                        io::ErrorKind::WriteZero,
-                        "failed to write whole buffer",
-                    ));
-                    return;
-                }
-                Ok(n) => {
-                    buf = &buf[n..];
-                    self.flushed += n;
-                }
-                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
-                Err(e) => {
-                    self.res = Err(e);
-                    return;
-                }
-            }
+        // SAFETY: We checked above that that N < self.buffer_empty().len(),
+        // and if isn't, flush ensures that our empty buffer is now BUF_SIZE.
+        // We produce a post-mono error if N > BUF_SIZE.
+        let buf = unsafe { self.buffer_empty().first_chunk_mut::<N>().unwrap_unchecked() };
+        let written = visitor(buf);
+        // We have to ensure that an errant visitor cannot cause self.buffered to exeed BUF_SIZE.
+        if written > N {
+            Self::panic_invalid_write::<N>(written);
         }
+        self.buffered += written;
+    }
+
+    #[cold]
+    #[inline(never)]
+    fn panic_invalid_write<const N: usize>(written: usize) {
+        panic!("FileEncoder::write_with::<{N}> cannot be used to write {written} bytes");
+    }
+
+    /// Helper for calls where [`FileEncoder::write_with`] always writes the whole array.
+    #[inline]
+    pub fn write_array<const N: usize>(&mut self, buf: [u8; N]) {
+        self.write_with(|dest| {
+            *dest = buf;
+            N
+        })
     }
 
     pub fn finish(mut self) -> Result<usize, io::Error> {
         self.flush();
-
-        let res = std::mem::replace(&mut self.res, Ok(()));
-        res.map(|()| self.position())
+        match std::mem::replace(&mut self.res, Ok(())) {
+            Ok(()) => Ok(self.position()),
+            Err(e) => Err(e),
+        }
     }
 }
 
@@ -241,7 +160,7 @@ impl Drop for FileEncoder {
     fn drop(&mut self) {
         // Likely to be a no-op, because `finish` should have been called and
         // it also flushes. But do it just in case.
-        let _result = self.flush();
+        self.flush();
     }
 }
 
@@ -249,26 +168,7 @@ macro_rules! write_leb128 {
     ($this_fn:ident, $int_ty:ty, $write_leb_fn:ident) => {
         #[inline]
         fn $this_fn(&mut self, v: $int_ty) {
-            const MAX_ENCODED_LEN: usize = $crate::leb128::max_leb128_len::<$int_ty>();
-
-            let mut buffered = self.buffered;
-
-            // This can't overflow because BUF_SIZE and MAX_ENCODED_LEN are both
-            // quite small.
-            if std::intrinsics::unlikely(buffered + MAX_ENCODED_LEN > BUF_SIZE) {
-                self.flush();
-                buffered = 0;
-            }
-
-            // SAFETY: The above check and flush ensures that there is enough
-            // room to write the encoded value to the buffer.
-            let buf = unsafe {
-                &mut *(self.buf.as_mut_ptr().add(buffered)
-                    as *mut [MaybeUninit<u8>; MAX_ENCODED_LEN])
-            };
-
-            let encoded = leb128::$write_leb_fn(buf, v);
-            self.buffered = buffered + encoded.len();
+            self.write_with(|buf| leb128::$write_leb_fn(buf, v))
         }
     };
 }
@@ -281,12 +181,12 @@ impl Encoder for FileEncoder {
 
     #[inline]
     fn emit_u16(&mut self, v: u16) {
-        self.write_all(&v.to_le_bytes());
+        self.write_array(v.to_le_bytes());
     }
 
     #[inline]
     fn emit_u8(&mut self, v: u8) {
-        self.write_one(v);
+        self.write_array([v]);
     }
 
     write_leb128!(emit_isize, isize, write_isize_leb128);
@@ -296,7 +196,7 @@ impl Encoder for FileEncoder {
 
     #[inline]
     fn emit_i16(&mut self, v: i16) {
-        self.write_all(&v.to_le_bytes());
+        self.write_array(v.to_le_bytes());
     }
 
     #[inline]
@@ -495,7 +395,7 @@ impl Encodable<FileEncoder> for IntEncodedWithFixedSize {
     #[inline]
     fn encode(&self, e: &mut FileEncoder) {
         let _start_pos = e.position();
-        e.emit_raw_bytes(&self.0.to_le_bytes());
+        e.write_array(self.0.to_le_bytes());
         let _end_pos = e.position();
         debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE);
     }
diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs
index 7872e778431..dc9b32a968b 100644
--- a/compiler/rustc_serialize/tests/leb128.rs
+++ b/compiler/rustc_serialize/tests/leb128.rs
@@ -1,8 +1,4 @@
-#![feature(maybe_uninit_slice)]
-#![feature(maybe_uninit_uninit_array)]
-
 use rustc_serialize::leb128::*;
-use std::mem::MaybeUninit;
 use rustc_serialize::Decoder;
 
 macro_rules! impl_test_unsigned_leb128 {
@@ -24,9 +20,10 @@ macro_rules! impl_test_unsigned_leb128 {
 
             let mut stream = Vec::new();
 
+            let mut buf = Default::default();
             for &x in &values {
-                let mut buf = MaybeUninit::uninit_array();
-                stream.extend($write_fn_name(&mut buf, x));
+                let n = $write_fn_name(&mut buf, x);
+                stream.extend(&buf[..n]);
             }
 
             let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0);
@@ -70,9 +67,10 @@ macro_rules! impl_test_signed_leb128 {
 
             let mut stream = Vec::new();
 
+            let mut buf = Default::default();
             for &x in &values {
-                let mut buf = MaybeUninit::uninit_array();
-                stream.extend($write_fn_name(&mut buf, x));
+                let n = $write_fn_name(&mut buf, x);
+                stream.extend(&buf[..n]);
             }
 
             let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0);
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index f2c8f0bc198..5a6def1958b 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1478,15 +1478,12 @@ options! {
     dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED],
         "exclude the pass number when dumping MIR (used in tests) (default: no)"),
     dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
-        "in addition to `.mir` files, create graphviz `.dot` files (and with \
-        `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived \
-        coverage graph) (default: no)"),
+        "in addition to `.mir` files, create graphviz `.dot` files (default: no)"),
     dump_mir_spanview: Option<MirSpanview> = (None, parse_mir_spanview, [UNTRACKED],
         "in addition to `.mir` files, create `.html` files to view spans for \
         all `statement`s (including terminators), only `terminator` spans, or \
         computed `block` spans (one span encompassing a block's terminator and \
-        all statements). If `-Z instrument-coverage` is also enabled, create \
-        an additional `.html` file showing the computed coverage spans."),
+        all statements)."),
     dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
         parse_switch_with_opt_path, [UNTRACKED],
         "output statistics about monomorphization collection"),
diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs
index 35e65c19be0..d8766cf8ce2 100644
--- a/compiler/rustc_smir/src/rustc_smir/alloc.rs
+++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs
@@ -1,4 +1,7 @@
-use rustc_middle::mir::interpret::{alloc_range, AllocRange, ConstValue, Pointer};
+use rustc_middle::mir::{
+    interpret::{alloc_range, AllocRange, Pointer},
+    ConstValue,
+};
 
 use crate::{
     rustc_smir::{Stable, Tables},
@@ -44,14 +47,12 @@ pub fn new_allocation<'tcx>(
                 tables.tcx.layout_of(rustc_middle::ty::ParamEnv::empty().and(ty)).unwrap().align;
             new_empty_allocation(align.abi)
         }
-        ConstValue::Slice { data, start, end } => {
+        ConstValue::Slice { data, meta } => {
             let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
-            let ptr = Pointer::new(alloc_id, rustc_target::abi::Size::from_bytes(start));
+            let ptr = Pointer::new(alloc_id, rustc_target::abi::Size::ZERO);
             let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
-            let scalar_len = rustc_middle::mir::interpret::Scalar::from_target_usize(
-                (end - start) as u64,
-                &tables.tcx,
-            );
+            let scalar_meta =
+                rustc_middle::mir::interpret::Scalar::from_target_usize(meta, &tables.tcx);
             let layout =
                 tables.tcx.layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty)).unwrap();
             let mut allocation =
@@ -66,8 +67,8 @@ pub fn new_allocation<'tcx>(
             allocation
                 .write_scalar(
                     &tables.tcx,
-                    alloc_range(tables.tcx.data_layout.pointer_size, scalar_len.size()),
-                    scalar_len,
+                    alloc_range(tables.tcx.data_layout.pointer_size, scalar_meta.size()),
+                    scalar_meta,
                 )
                 .unwrap();
             allocation.stable(tables)
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 68727a6c40e..0b575c13adf 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -127,10 +127,39 @@ impl FileLoader for RealFileLoader {
 
         let mut bytes = Lrc::new_uninit_slice(len as usize);
         let mut buf = BorrowedBuf::from(Lrc::get_mut(&mut bytes).unwrap());
-        file.read_buf_exact(buf.unfilled())?;
+        match file.read_buf_exact(buf.unfilled()) {
+            Ok(()) => {}
+            Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
+                drop(bytes);
+                return fs::read(path).map(Vec::into);
+            }
+            Err(e) => return Err(e),
+        }
         // SAFETY: If the read_buf_exact call returns Ok(()), then we have
         // read len bytes and initialized the buffer.
-        Ok(unsafe { bytes.assume_init() })
+        let bytes = unsafe { bytes.assume_init() };
+
+        // At this point, we've read all the bytes that filesystem metadata reported exist.
+        // But we are not guaranteed to be at the end of the file, because we did not attempt to do
+        // a read with a non-zero-sized buffer and get Ok(0).
+        // So we do small read to a fixed-size buffer. If the read returns no bytes then we're
+        // already done, and we just return the Lrc we built above.
+        // If the read returns bytes however, we just fall back to reading into a Vec then turning
+        // that into an Lrc, losing our nice peak memory behavior. This fallback code path should
+        // be rarely exercised.
+
+        let mut probe = [0u8; 32];
+        let n = loop {
+            match file.read(&mut probe) {
+                Ok(0) => return Ok(bytes),
+                Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
+                Err(e) => return Err(e),
+                Ok(n) => break n,
+            }
+        };
+        let mut bytes: Vec<u8> = bytes.iter().copied().chain(probe[..n].iter().copied()).collect();
+        file.read_to_end(&mut bytes)?;
+        Ok(bytes.into())
     }
 }
 
diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs
index e393db02064..a12f50c87a2 100644
--- a/compiler/rustc_span/src/source_map/tests.rs
+++ b/compiler/rustc_span/src/source_map/tests.rs
@@ -567,3 +567,30 @@ fn test_next_point() {
     assert_eq!(span.hi().0, 6);
     assert!(sm.span_to_snippet(span).is_err());
 }
+
+#[cfg(target_os = "linux")]
+#[test]
+fn read_binary_file_handles_lying_stat() {
+    // read_binary_file tries to read the contents of a file into an Lrc<[u8]> while
+    // never having two copies of the data in memory at once. This is an optimization
+    // to support include_bytes! with large files. But since Rust allocators are
+    // sensitive to alignment, our implementation can't be bootstrapped off calling
+    // std::fs::read. So we test that we have the same behavior even on files where
+    // fs::metadata lies.
+
+    // stat always says that /proc/self/cmdline is length 0, but it isn't.
+    let cmdline = Path::new("/proc/self/cmdline");
+    let len = std::fs::metadata(cmdline).unwrap().len() as usize;
+    let real = std::fs::read(cmdline).unwrap();
+    assert!(len < real.len());
+    let bin = RealFileLoader.read_binary_file(cmdline).unwrap();
+    assert_eq!(&real[..], &bin[..]);
+
+    // stat always says that /sys/devices/system/cpu/kernel_max is the size of a block.
+    let kernel_max = Path::new("/sys/devices/system/cpu/kernel_max");
+    let len = std::fs::metadata(kernel_max).unwrap().len() as usize;
+    let real = std::fs::read(kernel_max).unwrap();
+    assert!(len > real.len());
+    let bin = RealFileLoader.read_binary_file(kernel_max).unwrap();
+    assert_eq!(&real[..], &bin[..]);
+}
diff --git a/compiler/rustc_target/src/abi/call/loongarch.rs b/compiler/rustc_target/src/abi/call/loongarch.rs
index 247256f076b..e649d58bbca 100644
--- a/compiler/rustc_target/src/abi/call/loongarch.rs
+++ b/compiler/rustc_target/src/abi/call/loongarch.rs
@@ -83,6 +83,17 @@ where
             }
             FieldsShape::Union(_) => {
                 if !arg_layout.is_zst() {
+                    if arg_layout.is_transparent() {
+                        let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1;
+                        return should_use_fp_conv_helper(
+                            cx,
+                            &non_1zst_elem,
+                            xlen,
+                            flen,
+                            field1_kind,
+                            field2_kind,
+                        );
+                    }
                     return Err(CannotUseFpConv);
                 }
             }
diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs
index d90dce2a087..93a2045632a 100644
--- a/compiler/rustc_target/src/abi/call/riscv.rs
+++ b/compiler/rustc_target/src/abi/call/riscv.rs
@@ -89,6 +89,17 @@ where
             }
             FieldsShape::Union(_) => {
                 if !arg_layout.is_zst() {
+                    if arg_layout.is_transparent() {
+                        let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1;
+                        return should_use_fp_conv_helper(
+                            cx,
+                            &non_1zst_elem,
+                            xlen,
+                            flen,
+                            field1_kind,
+                            field2_kind,
+                        );
+                    }
                     return Err(CannotUseFpConv);
                 }
             }
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 636adcf6b17..74fe98920c4 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -66,6 +66,7 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
     fn is_never(this: TyAndLayout<'a, Self>) -> bool;
     fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
     fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
+    fn is_transparent(this: TyAndLayout<'a, Self>) -> bool;
 }
 
 impl<'a, Ty> TyAndLayout<'a, Ty> {
@@ -136,6 +137,13 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         Ty::is_unit(self)
     }
 
+    pub fn is_transparent<C>(self) -> bool
+    where
+        Ty: TyAbiInterface<'a, C>,
+    {
+        Ty::is_transparent(self)
+    }
+
     pub fn offset_of_subfield<C>(self, cx: &C, indices: impl Iterator<Item = usize>) -> Size
     where
         Ty: TyAbiInterface<'a, C>,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 7941f64873b..066129d8e47 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -344,7 +344,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
     ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
         let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
-        let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, goal_evaluation_kind);
+        let mut goal_evaluation =
+            self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
         let encountered_overflow = self.search_graph.encountered_overflow();
         let canonical_response = EvalCtxt::evaluate_canonical_goal(
             self.tcx(),
@@ -568,7 +569,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
                 unconstrained_goal,
             )?;
-            self.add_goals(instantiate_goals);
+            self.nested_goals.goals.extend(instantiate_goals);
 
             // Finally, equate the goal's RHS with the unconstrained var.
             // We put the nested goals from this into goals instead of
@@ -605,7 +606,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
                 goal,
             )?;
-            self.add_goals(instantiate_goals);
+            self.nested_goals.goals.extend(instantiate_goals);
             if has_changed {
                 unchanged_certainty = None;
             }
@@ -613,7 +614,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             match certainty {
                 Certainty::Yes => {}
                 Certainty::Maybe(_) => {
-                    self.add_goal(goal);
+                    self.nested_goals.goals.push(goal);
                     unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
                 }
             }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index 790f9b840f2..b3f9218d761 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -10,17 +10,21 @@
 //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
 use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
 use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
-use crate::solve::{response_no_constraints_raw, CanonicalResponse, QueryResult, Response};
+use crate::solve::{
+    inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response,
+};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_index::IndexVec;
 use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
 use rustc_infer::infer::canonical::CanonicalVarValues;
 use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
-use rustc_infer::infer::InferCtxt;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_middle::infer::canonical::Canonical;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{
     ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
 };
+use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{
     self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
     TypeVisitableExt,
@@ -29,6 +33,22 @@ use rustc_span::DUMMY_SP;
 use std::iter;
 use std::ops::Deref;
 
+trait ResponseT<'tcx> {
+    fn var_values(&self) -> CanonicalVarValues<'tcx>;
+}
+
+impl<'tcx> ResponseT<'tcx> for Response<'tcx> {
+    fn var_values(&self) -> CanonicalVarValues<'tcx> {
+        self.var_values
+    }
+}
+
+impl<'tcx, T> ResponseT<'tcx> for inspect::State<'tcx, T> {
+    fn var_values(&self) -> CanonicalVarValues<'tcx> {
+        self.var_values
+    }
+}
+
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     /// Canonicalizes the goal remembering the original values
     /// for each bound variable.
@@ -188,12 +208,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         original_values: Vec<ty::GenericArg<'tcx>>,
         response: CanonicalResponse<'tcx>,
     ) -> Result<(Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
-        let substitution = self.compute_query_response_substitution(&original_values, &response);
+        let substitution =
+            Self::compute_query_response_substitution(self.infcx, &original_values, &response);
 
         let Response { var_values, external_constraints, certainty } =
             response.substitute(self.tcx(), &substitution);
 
-        let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?;
+        let nested_goals =
+            Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values)?;
 
         let ExternalConstraintsData { region_constraints, opaque_types } =
             external_constraints.deref();
@@ -206,21 +228,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     /// This returns the substitutions to instantiate the bound variables of
     /// the canonical response. This depends on the `original_values` for the
     /// bound variables.
-    fn compute_query_response_substitution(
-        &self,
+    fn compute_query_response_substitution<T: ResponseT<'tcx>>(
+        infcx: &InferCtxt<'tcx>,
         original_values: &[ty::GenericArg<'tcx>],
-        response: &CanonicalResponse<'tcx>,
+        response: &Canonical<'tcx, T>,
     ) -> CanonicalVarValues<'tcx> {
         // FIXME: Longterm canonical queries should deal with all placeholders
         // created inside of the query directly instead of returning them to the
         // caller.
-        let prev_universe = self.infcx.universe();
+        let prev_universe = infcx.universe();
         let universes_created_in_query = response.max_universe.index();
         for _ in 0..universes_created_in_query {
-            self.infcx.create_next_universe();
+            infcx.create_next_universe();
         }
 
-        let var_values = response.value.var_values;
+        let var_values = response.value.var_values();
         assert_eq!(original_values.len(), var_values.len());
 
         // If the query did not make progress with constraining inference variables,
@@ -254,13 +276,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             }
         }
 
-        let var_values = self.tcx().mk_args_from_iter(response.variables.iter().enumerate().map(
+        let var_values = infcx.tcx.mk_args_from_iter(response.variables.iter().enumerate().map(
             |(index, info)| {
                 if info.universe() != ty::UniverseIndex::ROOT {
                     // A variable from inside a binder of the query. While ideally these shouldn't
                     // exist at all (see the FIXME at the start of this method), we have to deal with
                     // them for now.
-                    self.infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| {
+                    infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| {
                         ty::UniverseIndex::from(prev_universe.index() + idx.index())
                     })
                 } else if info.is_existential() {
@@ -274,7 +296,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     if let Some(v) = opt_values[BoundVar::from_usize(index)] {
                         v
                     } else {
-                        self.infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
+                        infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
                     }
                 } else {
                     // For placeholders which were already part of the input, we simply map this
@@ -287,9 +309,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         CanonicalVarValues { var_values }
     }
 
-    #[instrument(level = "debug", skip(self, param_env), ret)]
+    #[instrument(level = "debug", skip(infcx, param_env), ret)]
     fn unify_query_var_values(
-        &self,
+        infcx: &InferCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         original_values: &[ty::GenericArg<'tcx>],
         var_values: CanonicalVarValues<'tcx>,
@@ -298,7 +320,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         let mut nested_goals = vec![];
         for (&orig, response) in iter::zip(original_values, var_values.var_values) {
-            nested_goals.extend(self.eq_and_get_goals(param_env, orig, response)?);
+            nested_goals.extend(
+                infcx
+                    .at(&ObligationCause::dummy(), param_env)
+                    .eq(DefineOpaqueTypes::No, orig, response)
+                    .map(|InferOk { value: (), obligations }| {
+                        obligations.into_iter().map(|o| Goal::from(o))
+                    })
+                    .map_err(|e| {
+                        debug!(?e, "failed to equate");
+                        NoSolution
+                    })?,
+            );
         }
 
         Ok(nested_goals)
@@ -403,3 +436,35 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> {
         }
     }
 }
+
+impl<'tcx> inspect::ProofTreeBuilder<'tcx> {
+    pub fn make_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>(
+        ecx: &EvalCtxt<'_, 'tcx>,
+        data: T,
+    ) -> inspect::CanonicalState<'tcx, T> {
+        let state = inspect::State { var_values: ecx.var_values, data };
+        let state = state.fold_with(&mut EagerResolver { infcx: ecx.infcx });
+        Canonicalizer::canonicalize(
+            ecx.infcx,
+            CanonicalizeMode::Response { max_input_universe: ecx.max_input_universe },
+            &mut vec![],
+            state,
+        )
+    }
+
+    pub fn instantiate_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &InferCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        original_values: &[ty::GenericArg<'tcx>],
+        state: inspect::CanonicalState<'tcx, T>,
+    ) -> Result<(Vec<Goal<'tcx, ty::Predicate<'tcx>>>, T), NoSolution> {
+        let substitution =
+            EvalCtxt::compute_query_response_substitution(infcx, original_values, &state);
+
+        let inspect::State { var_values, data } = state.substitute(infcx.tcx, &substitution);
+
+        let nested_goals =
+            EvalCtxt::unify_query_var_values(infcx, param_env, original_values, var_values)?;
+        Ok((nested_goals, data))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
new file mode 100644
index 00000000000..15c8d9e5bcb
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -0,0 +1,235 @@
+/// An infrastructure to mechanically analyse proof trees.
+///
+/// It is unavoidable that this representation is somewhat
+/// lossy as it should hide quite a few semantically relevant things,
+/// e.g. canonicalization and the order of nested goals.
+///
+/// @lcnr: However, a lot of the weirdness here is not strictly necessary
+/// and could be improved in the future. This is mostly good enough for
+/// coherence right now and was annoying to implement, so I am leaving it
+/// as is until we start using it for something else.
+use std::ops::ControlFlow;
+
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{inspect, QueryResult};
+use rustc_middle::traits::solve::{Certainty, Goal};
+use rustc_middle::ty;
+
+use crate::solve::inspect::ProofTreeBuilder;
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
+
+pub struct InspectGoal<'a, 'tcx> {
+    infcx: &'a InferCtxt<'tcx>,
+    depth: usize,
+    orig_values: &'a [ty::GenericArg<'tcx>],
+    goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    evaluation: &'a inspect::GoalEvaluation<'tcx>,
+}
+
+pub struct InspectCandidate<'a, 'tcx> {
+    goal: &'a InspectGoal<'a, 'tcx>,
+    kind: inspect::ProbeKind<'tcx>,
+    nested_goals: Vec<inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>>,
+    result: QueryResult<'tcx>,
+}
+
+impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
+    pub fn infcx(&self) -> &'a InferCtxt<'tcx> {
+        self.goal.infcx
+    }
+
+    pub fn kind(&self) -> inspect::ProbeKind<'tcx> {
+        self.kind
+    }
+
+    pub fn result(&self) -> Result<Certainty, NoSolution> {
+        self.result.map(|c| c.value.certainty)
+    }
+
+    /// Visit the nested goals of this candidate.
+    ///
+    /// FIXME(@lcnr): we have to slightly adapt this API
+    /// to also use it to compute the most relevant goal
+    /// for fulfillment errors. Will do that once we actually
+    /// need it.
+    pub fn visit_nested<V: ProofTreeVisitor<'tcx>>(
+        &self,
+        visitor: &mut V,
+    ) -> ControlFlow<V::BreakTy> {
+        // HACK: An arbitrary cutoff to avoid dealing with overflow and cycles.
+        if self.goal.depth >= 10 {
+            let infcx = self.goal.infcx;
+            infcx.probe(|_| {
+                let mut instantiated_goals = vec![];
+                for goal in &self.nested_goals {
+                    let goal = match ProofTreeBuilder::instantiate_canonical_state(
+                        infcx,
+                        self.goal.goal.param_env,
+                        self.goal.orig_values,
+                        *goal,
+                    ) {
+                        Ok((_goals, goal)) => goal,
+                        Err(NoSolution) => {
+                            warn!(
+                                "unexpected failure when instantiating {:?}: {:?}",
+                                goal, self.nested_goals
+                            );
+                            return ControlFlow::Continue(());
+                        }
+                    };
+                    instantiated_goals.push(goal);
+                }
+
+                for &goal in &instantiated_goals {
+                    let (_, proof_tree) =
+                        infcx.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
+                    let proof_tree = proof_tree.unwrap();
+                    visitor.visit_goal(&InspectGoal::new(
+                        infcx,
+                        self.goal.depth + 1,
+                        &proof_tree,
+                    ))?;
+                }
+
+                ControlFlow::Continue(())
+            })?;
+        }
+        ControlFlow::Continue(())
+    }
+}
+
+impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
+    pub fn infcx(&self) -> &'a InferCtxt<'tcx> {
+        self.infcx
+    }
+
+    pub fn goal(&self) -> Goal<'tcx, ty::Predicate<'tcx>> {
+        self.goal
+    }
+
+    pub fn result(&self) -> Result<Certainty, NoSolution> {
+        self.evaluation.evaluation.result.map(|c| c.value.certainty)
+    }
+
+    fn candidates_recur(
+        &'a self,
+        candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
+        nested_goals: &mut Vec<inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>>,
+        probe: &inspect::Probe<'tcx>,
+    ) {
+        for step in &probe.steps {
+            match step {
+                &inspect::ProbeStep::AddGoal(goal) => nested_goals.push(goal),
+                inspect::ProbeStep::EvaluateGoals(_) => (),
+                inspect::ProbeStep::NestedProbe(ref probe) => {
+                    // Nested probes have to prove goals added in their parent
+                    // but do not leak them, so we truncate the added goals
+                    // afterwards.
+                    let num_goals = nested_goals.len();
+                    self.candidates_recur(candidates, nested_goals, probe);
+                    nested_goals.truncate(num_goals);
+                }
+            }
+        }
+
+        match probe.kind {
+            inspect::ProbeKind::NormalizedSelfTyAssembly
+            | inspect::ProbeKind::UnsizeAssembly
+            | inspect::ProbeKind::UpcastProjectionCompatibility => (),
+            // We add a candidate for the root evaluation if there
+            // is only one way to prove a given goal, e.g. for `WellFormed`.
+            //
+            // FIXME: This is currently wrong if we don't even try any
+            // candidates, e.g. for a trait goal, as in this case `candidates` is
+            // actually supposed to be empty.
+            inspect::ProbeKind::Root { result } => {
+                if candidates.is_empty() {
+                    candidates.push(InspectCandidate {
+                        goal: self,
+                        kind: probe.kind,
+                        nested_goals: nested_goals.clone(),
+                        result,
+                    });
+                }
+            }
+            inspect::ProbeKind::MiscCandidate { name: _, result }
+            | inspect::ProbeKind::TraitCandidate { source: _, result } => {
+                candidates.push(InspectCandidate {
+                    goal: self,
+                    kind: probe.kind,
+                    nested_goals: nested_goals.clone(),
+                    result,
+                });
+            }
+        }
+    }
+
+    pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
+        let mut candidates = vec![];
+        let last_eval_step = match self.evaluation.evaluation.kind {
+            inspect::CanonicalGoalEvaluationKind::Overflow
+            | inspect::CanonicalGoalEvaluationKind::CacheHit(_) => {
+                warn!("unexpected root evaluation: {:?}", self.evaluation);
+                return vec![];
+            }
+            inspect::CanonicalGoalEvaluationKind::Uncached { ref revisions } => {
+                if let Some(last) = revisions.last() {
+                    last
+                } else {
+                    return vec![];
+                }
+            }
+        };
+
+        let mut nested_goals = vec![];
+        self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step.evaluation);
+
+        candidates
+    }
+
+    fn new(
+        infcx: &'a InferCtxt<'tcx>,
+        depth: usize,
+        root: &'a inspect::GoalEvaluation<'tcx>,
+    ) -> Self {
+        match root.kind {
+            inspect::GoalEvaluationKind::Root { ref orig_values } => InspectGoal {
+                infcx,
+                depth,
+                orig_values,
+                goal: infcx.resolve_vars_if_possible(root.uncanonicalized_goal),
+                evaluation: root,
+            },
+            inspect::GoalEvaluationKind::Nested { .. } => unreachable!(),
+        }
+    }
+}
+
+/// The public API to interact with proof trees.
+pub trait ProofTreeVisitor<'tcx> {
+    type BreakTy;
+
+    fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy>;
+}
+
+pub trait ProofTreeInferCtxtExt<'tcx> {
+    fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        visitor: &mut V,
+    ) -> ControlFlow<V::BreakTy>;
+}
+
+impl<'tcx> ProofTreeInferCtxtExt<'tcx> for InferCtxt<'tcx> {
+    fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        visitor: &mut V,
+    ) -> ControlFlow<V::BreakTy> {
+        let (_, proof_tree) =
+            self.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
+        let proof_tree = proof_tree.unwrap();
+        visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
index 749bba33c9b..2eba98b02e9 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
@@ -1,28 +1,107 @@
+//! Building proof trees incrementally during trait solving.
+//!
+//! This code is *a bit* of a mess and can hopefully be
+//! mostly ignored. For a general overview of how it works,
+//! see the comment on [ProofTreeBuilder].
 use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::solve::inspect::{self, CacheHit, ProbeKind};
 use rustc_middle::traits::solve::{
     CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
 };
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::config::DumpSolverProofTree;
 
-use super::eval_ctxt::UseGlobalCache;
-use super::{GenerateProofTree, GoalEvaluationKind};
+use crate::solve::eval_ctxt::UseGlobalCache;
+use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree};
+
+/// The core data structure when building proof trees.
+///
+/// In case the current evaluation does not generate a proof
+/// tree, `state` is simply `None` and we avoid any work.
+///
+/// The possible states of the solver are represented via
+/// variants of [DebugSolver]. For any nested computation we call
+/// `ProofTreeBuilder::new_nested_computation_kind` which
+/// creates a new `ProofTreeBuilder` to temporarily replace the
+/// current one. Once that nested computation is done,
+/// `ProofTreeBuilder::nested_computation_kind` is called
+/// to add the finished nested evaluation to the parent.
+///
+/// We provide additional information to the current state
+/// by calling methods such as `ProofTreeBuilder::probe_kind`.
+///
+/// The actual structure closely mirrors the finished proof
+/// trees. At the end of trait solving `ProofTreeBuilder::finalize`
+/// is called to recursively convert the whole structure to a
+/// finished proof tree.
+pub(in crate::solve) struct ProofTreeBuilder<'tcx> {
+    state: Option<Box<BuilderData<'tcx>>>,
+}
+
+struct BuilderData<'tcx> {
+    tree: DebugSolver<'tcx>,
+    use_global_cache: UseGlobalCache,
+}
+
+/// The current state of the proof tree builder, at most places
+/// in the code, only one or two variants are actually possible.
+///
+/// We simply ICE in case that assumption is broken.
+#[derive(Debug)]
+enum DebugSolver<'tcx> {
+    Root,
+    GoalEvaluation(WipGoalEvaluation<'tcx>),
+    CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>),
+    AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
+    GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
+    Probe(WipProbe<'tcx>),
+}
+
+impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::GoalEvaluation(g)
+    }
+}
+
+impl<'tcx> From<WipCanonicalGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipCanonicalGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::CanonicalGoalEvaluation(g)
+    }
+}
+
+impl<'tcx> From<WipAddedGoalsEvaluation<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::AddedGoalsEvaluation(g)
+    }
+}
+
+impl<'tcx> From<WipGoalEvaluationStep<'tcx>> for DebugSolver<'tcx> {
+    fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::GoalEvaluationStep(g)
+    }
+}
+
+impl<'tcx> From<WipProbe<'tcx>> for DebugSolver<'tcx> {
+    fn from(p: WipProbe<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::Probe(p)
+    }
+}
 
 #[derive(Eq, PartialEq, Debug)]
-pub struct WipGoalEvaluation<'tcx> {
+struct WipGoalEvaluation<'tcx> {
     pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    pub kind: WipGoalEvaluationKind,
+    pub kind: WipGoalEvaluationKind<'tcx>,
     pub evaluation: Option<WipCanonicalGoalEvaluation<'tcx>>,
     pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
 }
 
 impl<'tcx> WipGoalEvaluation<'tcx> {
-    pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
+    fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
         inspect::GoalEvaluation {
             uncanonicalized_goal: self.uncanonicalized_goal,
             kind: match self.kind {
-                WipGoalEvaluationKind::Root => inspect::GoalEvaluationKind::Root,
+                WipGoalEvaluationKind::Root { orig_values } => {
+                    inspect::GoalEvaluationKind::Root { orig_values }
+                }
                 WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => {
                     inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack }
                 }
@@ -34,27 +113,27 @@ impl<'tcx> WipGoalEvaluation<'tcx> {
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub enum WipGoalEvaluationKind {
-    Root,
+pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> {
+    Root { orig_values: Vec<ty::GenericArg<'tcx>> },
     Nested { is_normalizes_to_hack: IsNormalizesToHack },
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub enum WipCanonicalGoalEvaluationKind {
+pub(in crate::solve) enum WipCanonicalGoalEvaluationKind {
     Overflow,
-    CacheHit(CacheHit),
+    CacheHit(inspect::CacheHit),
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub struct WipCanonicalGoalEvaluation<'tcx> {
-    pub goal: CanonicalInput<'tcx>,
-    pub kind: Option<WipCanonicalGoalEvaluationKind>,
-    pub revisions: Vec<WipGoalEvaluationStep<'tcx>>,
-    pub result: Option<QueryResult<'tcx>>,
+struct WipCanonicalGoalEvaluation<'tcx> {
+    goal: CanonicalInput<'tcx>,
+    kind: Option<WipCanonicalGoalEvaluationKind>,
+    revisions: Vec<WipGoalEvaluationStep<'tcx>>,
+    result: Option<QueryResult<'tcx>>,
 }
 
 impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
-    pub fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
+    fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
         let kind = match self.kind {
             Some(WipCanonicalGoalEvaluationKind::Overflow) => {
                 inspect::CanonicalGoalEvaluationKind::Overflow
@@ -76,13 +155,13 @@ impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub struct WipAddedGoalsEvaluation<'tcx> {
-    pub evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>,
-    pub result: Option<Result<Certainty, NoSolution>>,
+struct WipAddedGoalsEvaluation<'tcx> {
+    evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>,
+    result: Option<Result<Certainty, NoSolution>>,
 }
 
 impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
-    pub fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> {
+    fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> {
         inspect::AddedGoalsEvaluation {
             evaluations: self
                 .evaluations
@@ -97,17 +176,17 @@ impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub struct WipGoalEvaluationStep<'tcx> {
-    pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+struct WipGoalEvaluationStep<'tcx> {
+    instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
 
-    pub evaluation: WipProbe<'tcx>,
+    evaluation: WipProbe<'tcx>,
 }
 
 impl<'tcx> WipGoalEvaluationStep<'tcx> {
-    pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> {
+    fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> {
         let evaluation = self.evaluation.finalize();
         match evaluation.kind {
-            ProbeKind::Root { .. } => (),
+            inspect::ProbeKind::Root { .. } => (),
             _ => unreachable!("unexpected root evaluation: {evaluation:?}"),
         }
         inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation }
@@ -115,13 +194,13 @@ impl<'tcx> WipGoalEvaluationStep<'tcx> {
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub struct WipProbe<'tcx> {
+struct WipProbe<'tcx> {
     pub steps: Vec<WipProbeStep<'tcx>>,
-    pub kind: Option<ProbeKind<'tcx>>,
+    pub kind: Option<inspect::ProbeKind<'tcx>>,
 }
 
 impl<'tcx> WipProbe<'tcx> {
-    pub fn finalize(self) -> inspect::Probe<'tcx> {
+    fn finalize(self) -> inspect::Probe<'tcx> {
         inspect::Probe {
             steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(),
             kind: self.kind.unwrap(),
@@ -130,14 +209,14 @@ impl<'tcx> WipProbe<'tcx> {
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub enum WipProbeStep<'tcx> {
-    AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
+enum WipProbeStep<'tcx> {
+    AddGoal(inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
     EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
     NestedProbe(WipProbe<'tcx>),
 }
 
 impl<'tcx> WipProbeStep<'tcx> {
-    pub fn finalize(self) -> inspect::ProbeStep<'tcx> {
+    fn finalize(self) -> inspect::ProbeStep<'tcx> {
         match self {
             WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal),
             WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
@@ -146,55 +225,6 @@ impl<'tcx> WipProbeStep<'tcx> {
     }
 }
 
-#[derive(Debug)]
-pub enum DebugSolver<'tcx> {
-    Root,
-    GoalEvaluation(WipGoalEvaluation<'tcx>),
-    CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>),
-    AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
-    GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
-    Probe(WipProbe<'tcx>),
-}
-
-impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
-    fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
-        DebugSolver::GoalEvaluation(g)
-    }
-}
-
-impl<'tcx> From<WipCanonicalGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
-    fn from(g: WipCanonicalGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
-        DebugSolver::CanonicalGoalEvaluation(g)
-    }
-}
-
-impl<'tcx> From<WipAddedGoalsEvaluation<'tcx>> for DebugSolver<'tcx> {
-    fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> {
-        DebugSolver::AddedGoalsEvaluation(g)
-    }
-}
-
-impl<'tcx> From<WipGoalEvaluationStep<'tcx>> for DebugSolver<'tcx> {
-    fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> {
-        DebugSolver::GoalEvaluationStep(g)
-    }
-}
-
-impl<'tcx> From<WipProbe<'tcx>> for DebugSolver<'tcx> {
-    fn from(p: WipProbe<'tcx>) -> DebugSolver<'tcx> {
-        DebugSolver::Probe(p)
-    }
-}
-
-pub struct ProofTreeBuilder<'tcx> {
-    state: Option<Box<BuilderData<'tcx>>>,
-}
-
-struct BuilderData<'tcx> {
-    tree: DebugSolver<'tcx>,
-    use_global_cache: UseGlobalCache,
-}
-
 impl<'tcx> ProofTreeBuilder<'tcx> {
     fn new(
         state: impl Into<DebugSolver<'tcx>>,
@@ -273,16 +303,19 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         self.state.is_none()
     }
 
-    pub(super) fn new_goal_evaluation(
+    pub(in crate::solve) fn new_goal_evaluation(
         &mut self,
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
-        kind: GoalEvaluationKind,
+        orig_values: &[ty::GenericArg<'tcx>],
+        kind: solve::GoalEvaluationKind,
     ) -> ProofTreeBuilder<'tcx> {
         self.nested(|| WipGoalEvaluation {
             uncanonicalized_goal: goal,
             kind: match kind {
-                GoalEvaluationKind::Root => WipGoalEvaluationKind::Root,
-                GoalEvaluationKind::Nested { is_normalizes_to_hack } => {
+                solve::GoalEvaluationKind::Root => {
+                    WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() }
+                }
+                solve::GoalEvaluationKind::Nested { is_normalizes_to_hack } => {
                     WipGoalEvaluationKind::Nested { is_normalizes_to_hack }
                 }
             },
@@ -379,7 +412,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         self.nested(|| WipProbe { steps: vec![], kind: None })
     }
 
-    pub fn probe_kind(&mut self, probe_kind: ProbeKind<'tcx>) {
+    pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<'tcx>) {
         if let Some(this) = self.as_mut() {
             match this {
                 DebugSolver::Probe(this) => {
@@ -390,18 +423,22 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
-        if let Some(this) = self.as_mut() {
-            match this {
-                DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
-                    evaluation: WipProbe { steps, .. },
-                    ..
-                })
-                | DebugSolver::Probe(WipProbe { steps, .. }) => {
-                    steps.push(WipProbeStep::AddGoal(goal))
-                }
-                _ => unreachable!(),
-            }
+    pub fn add_goal(ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
+        // Can't use `if let Some(this) = ecx.inspect.as_mut()` here because
+        // we have to immutably use the `EvalCtxt` for `make_canonical_state`.
+        if ecx.inspect.is_noop() {
+            return;
+        }
+
+        let goal = Self::make_canonical_state(ecx, goal);
+
+        match ecx.inspect.as_mut().unwrap() {
+            DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                evaluation: WipProbe { steps, .. },
+                ..
+            })
+            | DebugSolver::Probe(WipProbe { steps, .. }) => steps.push(WipProbeStep::AddGoal(goal)),
+            s => unreachable!("tried to add {goal:?} to {s:?}"),
         }
     }
 
@@ -471,7 +508,10 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
                 }
                 DebugSolver::GoalEvaluationStep(evaluation_step) => {
                     assert_eq!(
-                        evaluation_step.evaluation.kind.replace(ProbeKind::Root { result }),
+                        evaluation_step
+                            .evaluation
+                            .kind
+                            .replace(inspect::ProbeKind::Root { result }),
                         None
                     );
                 }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
new file mode 100644
index 00000000000..60d52305a6b
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
@@ -0,0 +1,7 @@
+pub use rustc_middle::traits::solve::inspect::*;
+
+mod build;
+pub(in crate::solve) use build::*;
+
+mod analyse;
+pub use analyse::*;
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index bd612ce4778..77a3b5e1284 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -235,7 +235,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
     #[instrument(level = "debug", skip(self))]
     fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
-        self.inspect.add_goal(goal);
+        inspect::ProofTreeBuilder::add_goal(self, goal);
         self.nested_goals.goals.push(goal);
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 3fe914e50be..f6e781b87ba 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -346,13 +346,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output])
         });
 
-        let pred = tupled_inputs_and_output
-            .map_bound(|(inputs, output)| ty::ProjectionPredicate {
+        let pred = ty::Clause::from_projection_clause(
+            tcx,
+            tupled_inputs_and_output.map_bound(|(inputs, output)| ty::ProjectionPredicate {
                 projection_ty: tcx
                     .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
                 term: output.into(),
-            })
-            .to_predicate(tcx);
+            }),
+        );
+
         // A built-in `Fn` impl only holds if the output is sized.
         // (FIXME: technically we only need to check this if the type is a fn ptr...)
         Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index e90723b0ee8..59f712721fb 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -6,9 +6,15 @@
 
 use crate::infer::outlives::env::OutlivesEnvironment;
 use crate::infer::InferOk;
+use crate::solve::inspect;
+use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
+use crate::traits::engine::TraitEngineExt;
 use crate::traits::outlives_bounds::InferCtxtExt as _;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
+use crate::traits::structural_normalize::StructurallyNormalizeExt;
 use crate::traits::util::impl_subject_and_oblig;
+use crate::traits::NormalizeExt;
 use crate::traits::SkipLeakCheck;
 use crate::traits::{
     self, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations,
@@ -18,10 +24,13 @@ use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::Diagnostic;
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::util;
+use rustc_infer::traits::{util, TraitEngine};
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal};
 use rustc_middle::traits::specialization_graph::OverlapMode;
 use rustc_middle::traits::DefiningAnchor;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
 use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE;
@@ -31,9 +40,6 @@ use std::fmt::Debug;
 use std::iter;
 use std::ops::ControlFlow;
 
-use super::query::evaluate_obligation::InferCtxtExt;
-use super::NormalizeExt;
-
 /// Whether we do the orphan check relative to this crate or
 /// to some remote crate.
 #[derive(Copy, Clone, Debug)]
@@ -205,19 +211,19 @@ fn overlap<'tcx>(
 
     // Equate the headers to find their intersection (the general type, with infer vars,
     // that may apply both impls).
-    let equate_obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?;
+    let mut obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?;
     debug!("overlap: unification check succeeded");
 
+    obligations.extend(
+        [&impl1_header.predicates, &impl2_header.predicates].into_iter().flatten().map(
+            |&predicate| Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate),
+        ),
+    );
+
     if overlap_mode.use_implicit_negative() {
         for mode in [TreatInductiveCycleAs::Ambig, TreatInductiveCycleAs::Recur] {
             if let Some(failing_obligation) = selcx.with_treat_inductive_cycle_as(mode, |selcx| {
-                impl_intersection_has_impossible_obligation(
-                    selcx,
-                    param_env,
-                    &impl1_header,
-                    &impl2_header,
-                    &equate_obligations,
-                )
+                impl_intersection_has_impossible_obligation(selcx, &obligations)
             }) {
                 if matches!(mode, TreatInductiveCycleAs::Recur) {
                     let first_local_impl = impl1_header
@@ -281,7 +287,14 @@ fn overlap<'tcx>(
         return None;
     }
 
-    let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
+    let intercrate_ambiguity_causes = if !overlap_mode.use_implicit_negative() {
+        Default::default()
+    } else if infcx.next_trait_solver() {
+        compute_intercrate_ambiguity_causes(&infcx, &obligations)
+    } else {
+        selcx.take_intercrate_ambiguity_causes()
+    };
+
     debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
     let involves_placeholder = infcx
         .inner
@@ -335,34 +348,24 @@ fn equate_impl_headers<'tcx>(
 /// of the two impls above to be empty.
 ///
 /// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
-fn impl_intersection_has_impossible_obligation<'cx, 'tcx>(
+fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    impl1_header: &ty::ImplHeader<'tcx>,
-    impl2_header: &ty::ImplHeader<'tcx>,
-    obligations: &PredicateObligations<'tcx>,
-) -> Option<PredicateObligation<'tcx>> {
+    obligations: &'a [PredicateObligation<'tcx>],
+) -> Option<&'a PredicateObligation<'tcx>> {
     let infcx = selcx.infcx;
 
-    [&impl1_header.predicates, &impl2_header.predicates]
-        .into_iter()
-        .flatten()
-        .map(|&predicate| {
-            Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate)
-        })
-        .chain(obligations.into_iter().cloned())
-        .find(|obligation: &PredicateObligation<'tcx>| {
-            if infcx.next_trait_solver() {
-                infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
-            } else {
-                // We use `evaluate_root_obligation` to correctly track intercrate
-                // ambiguity clauses. We cannot use this in the new solver.
-                selcx.evaluate_root_obligation(obligation).map_or(
-                    false, // Overflow has occurred, and treat the obligation as possibly holding.
-                    |result| !result.may_apply(),
-                )
-            }
-        })
+    obligations.iter().find(|obligation| {
+        if infcx.next_trait_solver() {
+            infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
+        } else {
+            // We use `evaluate_root_obligation` to correctly track intercrate
+            // ambiguity clauses. We cannot use this in the new solver.
+            selcx.evaluate_root_obligation(obligation).map_or(
+                false, // Overflow has occurred, and treat the obligation as possibly holding.
+                |result| !result.may_apply(),
+            )
+        }
+    })
 }
 
 /// Check if both impls can be satisfied by a common type by considering whether
@@ -882,3 +885,144 @@ where
         ControlFlow::Continue(())
     }
 }
+
+/// Compute the `intercrate_ambiguity_causes` for the new solver using
+/// "proof trees".
+///
+/// This is a bit scuffed but seems to be good enough, at least
+/// when looking at UI tests. Given that it is only used to improve
+/// diagnostics this is good enough. We can always improve it once there
+/// are test cases where it is currently not enough.
+fn compute_intercrate_ambiguity_causes<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    obligations: &[PredicateObligation<'tcx>],
+) -> FxIndexSet<IntercrateAmbiguityCause> {
+    let mut causes: FxIndexSet<IntercrateAmbiguityCause> = Default::default();
+
+    for obligation in obligations {
+        search_ambiguity_causes(infcx, obligation.clone().into(), &mut causes);
+    }
+
+    causes
+}
+
+struct AmbiguityCausesVisitor<'a> {
+    causes: &'a mut FxIndexSet<IntercrateAmbiguityCause>,
+}
+
+impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> {
+    type BreakTy = !;
+    fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy> {
+        let infcx = goal.infcx();
+        for cand in goal.candidates() {
+            cand.visit_nested(self)?;
+        }
+        // When searching for intercrate ambiguity causes, we only need to look
+        // at ambiguous goals, as for others the coherence unknowable candidate
+        // was irrelevant.
+        match goal.result() {
+            Ok(Certainty::Maybe(_)) => {}
+            Ok(Certainty::Yes) | Err(NoSolution) => return ControlFlow::Continue(()),
+        }
+
+        let Goal { param_env, predicate } = goal.goal();
+
+        // For bound predicates we simply call `infcx.replace_bound_vars_with_placeholders`
+        // and then prove the resulting predicate as a nested goal.
+        let trait_ref = match predicate.kind().no_bound_vars() {
+            Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,
+            Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) => {
+                proj.projection_ty.trait_ref(infcx.tcx)
+            }
+            _ => return ControlFlow::Continue(()),
+        };
+
+        let mut ambiguity_cause = None;
+        for cand in goal.candidates() {
+            // FIXME: boiiii, using string comparisions here sure is scuffed.
+            if let inspect::ProbeKind::MiscCandidate { name: "coherence unknowable", result: _ } =
+                cand.kind()
+            {
+                let lazily_normalize_ty = |ty: Ty<'tcx>| {
+                    let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx);
+                    if matches!(ty.kind(), ty::Alias(..)) {
+                        // FIXME(-Ztrait-solver=next-coherence): we currently don't
+                        // normalize opaque types here, resulting in diverging behavior
+                        // for TAITs.
+                        match infcx
+                            .at(&ObligationCause::dummy(), param_env)
+                            .structurally_normalize(ty, &mut *fulfill_cx)
+                        {
+                            Ok(ty) => Ok(ty),
+                            Err(_errs) => Err(()),
+                        }
+                    } else {
+                        Ok(ty)
+                    }
+                };
+
+                infcx.probe(|_| {
+                    match trait_ref_is_knowable(infcx.tcx, trait_ref, lazily_normalize_ty) {
+                        Err(()) => {}
+                        Ok(Ok(())) => warn!("expected an unknowable trait ref: {trait_ref:?}"),
+                        Ok(Err(conflict)) => {
+                            if !trait_ref.references_error() {
+                                let self_ty = trait_ref.self_ty();
+                                let (trait_desc, self_desc) = with_no_trimmed_paths!({
+                                    let trait_desc = trait_ref.print_only_trait_path().to_string();
+                                    let self_desc = self_ty
+                                        .has_concrete_skeleton()
+                                        .then(|| self_ty.to_string());
+                                    (trait_desc, self_desc)
+                                });
+                                ambiguity_cause = Some(match conflict {
+                                    Conflict::Upstream => {
+                                        IntercrateAmbiguityCause::UpstreamCrateUpdate {
+                                            trait_desc,
+                                            self_desc,
+                                        }
+                                    }
+                                    Conflict::Downstream => {
+                                        IntercrateAmbiguityCause::DownstreamCrate {
+                                            trait_desc,
+                                            self_desc,
+                                        }
+                                    }
+                                });
+                            }
+                        }
+                    }
+                })
+            } else {
+                match cand.result() {
+                    // We only add an ambiguity cause if the goal would otherwise
+                    // result in an error.
+                    //
+                    // FIXME: While this matches the behavior of the
+                    // old solver, it is not the only way in which the unknowable
+                    // candidates *weaken* coherence, they can also force otherwise
+                    // sucessful normalization to be ambiguous.
+                    Ok(Certainty::Maybe(_) | Certainty::Yes) => {
+                        ambiguity_cause = None;
+                        break;
+                    }
+                    Err(NoSolution) => continue,
+                }
+            }
+        }
+
+        if let Some(ambiguity_cause) = ambiguity_cause {
+            self.causes.insert(ambiguity_cause);
+        }
+
+        ControlFlow::Continue(())
+    }
+}
+
+fn search_ambiguity_causes<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    causes: &mut FxIndexSet<IntercrateAmbiguityCause>,
+) {
+    infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes });
+}
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 135410691f1..7d754cbafa3 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -986,6 +986,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             }
                         }
 
+                        self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause);
+
                         // Return early if the trait is Debug or Display and the invocation
                         // originates within a standard library macro, because the output
                         // is otherwise overwhelming and unhelpful (see #85844 for an
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 d6ac7208715..a08ebe5a9ea 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -406,6 +406,14 @@ pub trait TypeErrCtxtExt<'tcx> {
         candidate_impls: &[ImplCandidate<'tcx>],
         span: Span,
     );
+
+    fn explain_hrtb_projection(
+        &self,
+        diag: &mut Diagnostic,
+        pred: ty::PolyTraitPredicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: &ObligationCause<'tcx>,
+    );
 }
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -4027,6 +4035,71 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
         }
     }
+
+    fn explain_hrtb_projection(
+        &self,
+        diag: &mut Diagnostic,
+        pred: ty::PolyTraitPredicate<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: &ObligationCause<'tcx>,
+    ) {
+        if pred.skip_binder().has_escaping_bound_vars() && pred.skip_binder().has_non_region_infer()
+        {
+            self.probe(|_| {
+                let ocx = ObligationCtxt::new(self);
+                let pred = self.instantiate_binder_with_placeholders(pred);
+                let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred);
+                ocx.register_obligation(Obligation::new(
+                    self.tcx,
+                    ObligationCause::dummy(),
+                    param_env,
+                    pred,
+                ));
+                if !ocx.select_where_possible().is_empty() {
+                    // encountered errors.
+                    return;
+                }
+
+                if let ObligationCauseCode::FunctionArgumentObligation {
+                    call_hir_id,
+                    arg_hir_id,
+                    parent_code: _,
+                } = cause.code()
+                {
+                    let arg_span = self.tcx.hir().span(*arg_hir_id);
+                    let mut sp: MultiSpan = arg_span.into();
+
+                    sp.push_span_label(
+                        arg_span,
+                        "the trait solver is unable to infer the \
+                        generic types that should be inferred from this argument",
+                    );
+                    sp.push_span_label(
+                        self.tcx.hir().span(*call_hir_id),
+                        "add turbofish arguments to this call to \
+                        specify the types manually, even if it's redundant",
+                    );
+                    diag.span_note(
+                        sp,
+                        "this is a known limitation of the trait solver that \
+                        will be lifted in the future",
+                    );
+                } else {
+                    let mut sp: MultiSpan = cause.span.into();
+                    sp.push_span_label(
+                        cause.span,
+                        "try adding turbofish arguments to this expression to \
+                        specify the types manually, even if it's redundant",
+                    );
+                    diag.span_note(
+                        sp,
+                        "this is a known limitation of the trait solver that \
+                        will be lifted in the future",
+                    );
+                }
+            });
+        }
+    }
 }
 
 /// Add a hint to add a missing borrow or remove an unnecessary one.
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 6444c01a67b..93e8e1f4bb1 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1644,7 +1644,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
     let env_predicates = data
         .projection_bounds()
         .filter(|bound| bound.item_def_id() == obligation.predicate.def_id)
-        .map(|p| p.with_self_ty(tcx, object_ty).to_predicate(tcx));
+        .map(|p| ty::Clause::from_projection_clause(tcx, p.with_self_ty(tcx, object_ty)));
 
     assemble_candidates_from_predicates(
         selcx,
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
index d3c4dc45923..9d6be768901 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -22,9 +22,14 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
         assert!(!ty.is_ty_var(), "should have resolved vars before calling");
 
         if self.infcx.next_trait_solver() {
-            while let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, projection_ty) =
-                *ty.kind()
-            {
+            // FIXME(-Ztrait-solver=next): correctly handle
+            // overflow here.
+            for _ in 0..256 {
+                let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, projection_ty) = *ty.kind()
+                else {
+                    break;
+                };
+
                 let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::NormalizeProjectionType,
                     span: self.cause.span,
@@ -49,6 +54,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
                     break;
                 }
             }
+
             Ok(ty)
         } else {
             Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx))
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 0dbac56b47d..01bb1ca70eb 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -3,10 +3,13 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::query::{
     normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution,
 };
-use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext};
+use rustc_trait_selection::traits::{
+    self, FulfillmentErrorCode, ObligationCause, SelectionContext,
+};
 use std::sync::atomic::Ordering;
 
 pub(crate) fn provide(p: &mut Providers) {
@@ -40,6 +43,27 @@ fn normalize_projection_ty<'tcx>(
                 &mut obligations,
             );
             ocx.register_obligations(obligations);
+            // #112047: With projections and opaques, we are able to create opaques that
+            // are recursive (given some substitution of the opaque's type variables).
+            // In that case, we may only realize a cycle error when calling
+            // `normalize_erasing_regions` in mono.
+            if !ocx.infcx.next_trait_solver() {
+                let errors = ocx.select_where_possible();
+                if !errors.is_empty() {
+                    // Rustdoc may attempt to normalize type alias types which are not
+                    // well-formed. Rustdoc also normalizes types that are just not
+                    // well-formed, since we don't do as much HIR analysis (checking
+                    // that impl vars are constrained by the signature, for example).
+                    if !tcx.sess.opts.actually_rustdoc {
+                        for error in &errors {
+                            if let FulfillmentErrorCode::CodeCycle(cycle) = &error.code {
+                                ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
+                            }
+                        }
+                    }
+                    return Err(NoSolution);
+                }
+            }
             // FIXME(associated_const_equality): All users of normalize_projection_ty expected
             // a type, but there is the possibility it could've been a const now. Maybe change
             // it to a Term later?
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index da2958bf56e..91f1c21310e 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -141,11 +141,34 @@ fn resolve_associated_item<'tcx>(
                     false
                 }
             };
-
             if !eligible {
                 return Ok(None);
             }
 
+            // HACK: We may have overlapping `dyn Trait` built-in impls and
+            // user-provided blanket impls. Detect that case here, and return
+            // ambiguity.
+            //
+            // This should not affect totally monomorphized contexts, only
+            // resolve calls that happen polymorphically, such as the mir-inliner
+            // and const-prop (and also some lints).
+            let self_ty = rcvr_args.type_at(0);
+            if !self_ty.is_known_rigid() {
+                let predicates = tcx
+                    .predicates_of(impl_data.impl_def_id)
+                    .instantiate(tcx, impl_data.args)
+                    .predicates;
+                let sized_def_id = tcx.lang_items().sized_trait();
+                // If we find a `Self: Sized` bound on the item, then we know
+                // that `dyn Trait` can certainly never apply here.
+                if !predicates.into_iter().filter_map(ty::Clause::as_trait_clause).any(|clause| {
+                    Some(clause.def_id()) == sized_def_id
+                        && clause.skip_binder().self_ty() == self_ty
+                }) {
+                    return Ok(None);
+                }
+            }
+
             // Any final impl is required to define all associated items.
             if !leaf_def.item.defaultness(tcx).has_value() {
                 let guard = tcx.sess.delay_span_bug(
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index ba0258b63cb..2288d36df17 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -4,7 +4,7 @@ use rustc_hir::def::DefKind;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, EarlyBinder, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+    self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
 };
 use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_span::DUMMY_SP;
@@ -220,13 +220,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
             // strategy, then just reinterpret the associated type like an opaque :^)
             let default_ty = self.tcx.type_of(shifted_alias_ty.def_id).instantiate(self.tcx, shifted_alias_ty.args);
 
-            self.predicates.push(
-                ty::Binder::bind_with_vars(
-                    ty::ProjectionPredicate { projection_ty: shifted_alias_ty, term: default_ty.into() },
-                    self.bound_vars,
-                )
-                .to_predicate(self.tcx),
-            );
+            self.predicates.push(ty::Clause::from_projection_clause(self.tcx, ty::Binder::bind_with_vars(
+                ty::ProjectionPredicate { projection_ty: shifted_alias_ty, term: default_ty.into() },
+                self.bound_vars,
+            )));
 
             // We walk the *un-shifted* alias ty, because we're tracking the de bruijn
             // binder depth, and if we were to walk `shifted_alias_ty` instead, we'd
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index 7e4077db935..93a6716d7ab 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -214,6 +214,8 @@ impl CStr {
     /// * The memory referenced by the returned `CStr` must not be mutated for
     ///   the duration of lifetime `'a`.
     ///
+    /// * The nul terminator must be within `isize::MAX` from `ptr`
+    ///
     /// > **Note**: This operation is intended to be a 0-cost cast but it is
     /// > currently implemented with an up-front calculation of the length of
     /// > the string. This is not guaranteed to always be the case.
@@ -259,42 +261,16 @@ impl CStr {
     #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")]
     pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
         // SAFETY: The caller has provided a pointer that points to a valid C
-        // string with a NUL terminator of size less than `isize::MAX`, whose
-        // content remain valid and doesn't change for the lifetime of the
-        // returned `CStr`.
-        //
-        // Thus computing the length is fine (a NUL byte exists), the call to
-        // from_raw_parts is safe because we know the length is at most `isize::MAX`, meaning
-        // the call to `from_bytes_with_nul_unchecked` is correct.
+        // string with a NUL terminator less than `isize::MAX` from `ptr`.
+        let len = unsafe { const_strlen(ptr) };
+
+        // SAFETY: The caller has provided a valid pointer with length less than
+        // `isize::MAX`, so `from_raw_parts` is safe. The content remains valid
+        // and doesn't change for the lifetime of the returned `CStr`. This
+        // means the call to `from_bytes_with_nul_unchecked` is correct.
         //
         // The cast from c_char to u8 is ok because a c_char is always one byte.
-        unsafe {
-            const fn strlen_ct(s: *const c_char) -> usize {
-                let mut len = 0;
-
-                // SAFETY: Outer caller has provided a pointer to a valid C string.
-                while unsafe { *s.add(len) } != 0 {
-                    len += 1;
-                }
-
-                len
-            }
-
-            // `inline` is necessary for codegen to see strlen.
-            #[inline]
-            fn strlen_rt(s: *const c_char) -> usize {
-                extern "C" {
-                    /// Provided by libc or compiler_builtins.
-                    fn strlen(s: *const c_char) -> usize;
-                }
-
-                // SAFETY: Outer caller has provided a pointer to a valid C string.
-                unsafe { strlen(s) }
-            }
-
-            let len = intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt);
-            Self::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr.cast(), len + 1))
-        }
+        unsafe { Self::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr.cast(), len + 1)) }
     }
 
     /// Creates a C string wrapper from a byte slice with any number of nuls.
@@ -516,6 +492,34 @@ impl CStr {
         self.inner.as_ptr()
     }
 
+    /// Returns the length of `self`. Like C's `strlen`, this does not include the nul terminator.
+    ///
+    /// > **Note**: This method is currently implemented as a constant-time
+    /// > cast, but it is planned to alter its definition in the future to
+    /// > perform the length calculation whenever this method is called.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(cstr_count_bytes)]
+    ///
+    /// use std::ffi::CStr;
+    ///
+    /// let cstr = CStr::from_bytes_with_nul(b"foo\0").unwrap();
+    /// assert_eq!(cstr.count_bytes(), 3);
+    ///
+    /// let cstr = CStr::from_bytes_with_nul(b"\0").unwrap();
+    /// assert_eq!(cstr.count_bytes(), 0);
+    /// ```
+    #[inline]
+    #[must_use]
+    #[doc(alias("len", "strlen"))]
+    #[unstable(feature = "cstr_count_bytes", issue = "114441")]
+    #[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "113219")]
+    pub const fn count_bytes(&self) -> usize {
+        self.inner.len() - 1
+    }
+
     /// Returns `true` if `self.to_bytes()` has a length of 0.
     ///
     /// # Examples
@@ -682,3 +686,37 @@ impl AsRef<CStr> for CStr {
         self
     }
 }
+
+/// Calculate the length of a nul-terminated string. Defers to C's `strlen` when possible.
+///
+/// # Safety
+///
+/// The pointer must point to a valid buffer that contains a NUL terminator. The NUL must be
+/// located within `isize::MAX` from `ptr`.
+#[inline]
+const unsafe fn const_strlen(ptr: *const c_char) -> usize {
+    const fn strlen_ct(s: *const c_char) -> usize {
+        let mut len = 0;
+
+        // SAFETY: Outer caller has provided a pointer to a valid C string.
+        while unsafe { *s.add(len) } != 0 {
+            len += 1;
+        }
+
+        len
+    }
+
+    #[inline]
+    fn strlen_rt(s: *const c_char) -> usize {
+        extern "C" {
+            /// Provided by libc or compiler_builtins.
+            fn strlen(s: *const c_char) -> usize;
+        }
+
+        // SAFETY: Outer caller has provided a pointer to a valid C string.
+        unsafe { strlen(s) }
+    }
+
+    // SAFETY: the two functions always provide equivalent functionality
+    unsafe { intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt) }
+}
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 3144db19707..290f649f9ac 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -957,6 +957,7 @@ impl f32 {
         } else if self == other {
             if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
         } else {
+            // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
             self + other
         }
     }
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 20833defc41..7569d2cd6ca 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -968,6 +968,7 @@ impl f64 {
         } else if self == other {
             if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
         } else {
+            // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
             self + other
         }
     }
diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs
index 20be60d3535..386f5fcbd6a 100644
--- a/library/core/src/panic.rs
+++ b/library/core/src/panic.rs
@@ -99,7 +99,7 @@ pub macro unreachable_2021 {
 /// use.
 #[unstable(feature = "std_internals", issue = "none")]
 #[doc(hidden)]
-pub unsafe trait BoxMeUp {
+pub unsafe trait PanicPayload {
     /// Take full ownership of the contents.
     /// The return type is actually `Box<dyn Any + Send>`, but we cannot use `Box` in core.
     ///
@@ -107,7 +107,7 @@ pub unsafe trait BoxMeUp {
     /// Calling this method twice, or calling `get` after calling this method, is an error.
     ///
     /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only
-    /// gets a borrowed `dyn BoxMeUp`.
+    /// gets a borrowed `dyn PanicPayload`.
     fn take_box(&mut self) -> *mut (dyn Any + Send);
 
     /// Just borrow the contents.
diff --git a/library/panic_abort/src/android.rs b/library/panic_abort/src/android.rs
index 20b5b6b5146..47c22834597 100644
--- a/library/panic_abort/src/android.rs
+++ b/library/panic_abort/src/android.rs
@@ -1,6 +1,6 @@
 use alloc::string::String;
 use core::mem::transmute;
-use core::panic::BoxMeUp;
+use core::panic::PanicPayload;
 use core::ptr::copy_nonoverlapping;
 
 const ANDROID_SET_ABORT_MESSAGE: &[u8] = b"android_set_abort_message\0";
@@ -15,7 +15,7 @@ type SetAbortMessageType = unsafe extern "C" fn(*const libc::c_char) -> ();
 //
 // Weakly resolve the symbol for android_set_abort_message. This function is only available
 // for API >= 21.
-pub(crate) unsafe fn android_set_abort_message(payload: &mut dyn BoxMeUp) {
+pub(crate) unsafe fn android_set_abort_message(payload: &mut dyn PanicPayload) {
     let func_addr =
         libc::dlsym(libc::RTLD_DEFAULT, ANDROID_SET_ABORT_MESSAGE.as_ptr() as *const libc::c_char)
             as usize;
diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs
index 02534491da9..d675696f13f 100644
--- a/library/panic_abort/src/lib.rs
+++ b/library/panic_abort/src/lib.rs
@@ -20,7 +20,7 @@
 mod android;
 
 use core::any::Any;
-use core::panic::BoxMeUp;
+use core::panic::PanicPayload;
 
 #[rustc_std_internal_symbol]
 #[allow(improper_ctypes_definitions)]
@@ -30,7 +30,7 @@ pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Sen
 
 // "Leak" the payload and shim to the relevant abort on the platform in question.
 #[rustc_std_internal_symbol]
-pub unsafe fn __rust_start_panic(_payload: &mut dyn BoxMeUp) -> u32 {
+pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
     // Android has the ability to attach a message as part of the abort.
     #[cfg(target_os = "android")]
     android::android_set_abort_message(_payload);
@@ -43,7 +43,8 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn BoxMeUp) -> u32 {
                 libc::abort();
             }
         } else if #[cfg(any(target_os = "hermit",
-                            all(target_vendor = "fortanix", target_env = "sgx")
+                            all(target_vendor = "fortanix", target_env = "sgx"),
+                            target_os = "xous"
         ))] {
             unsafe fn abort() -> ! {
                 // call std::sys::abort_internal
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index e7d34daa079..9363fde5de2 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -29,7 +29,7 @@
 
 use alloc::boxed::Box;
 use core::any::Any;
-use core::panic::BoxMeUp;
+use core::panic::PanicPayload;
 
 cfg_if::cfg_if! {
     if #[cfg(target_os = "emscripten")] {
@@ -99,7 +99,7 @@ pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any
 // Entry point for raising an exception, just delegates to the platform-specific
 // implementation.
 #[rustc_std_internal_symbol]
-pub unsafe fn __rust_start_panic(payload: &mut dyn BoxMeUp) -> u32 {
+pub unsafe fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32 {
     let payload = Box::from_raw(payload.take_box());
 
     imp::panic(payload)
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 33c9c6e63c1..e8f642586cd 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
 panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core", public = true }
-libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true }
+libc = { version = "0.2.148", default-features = false, features = ['rustc-dep-of-std'], public = true }
 compiler_builtins = { version = "0.1.100" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
@@ -36,8 +36,8 @@ object = { version = "0.32.0", default-features = false, optional = true, featur
 rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
 rand_xorshift = "0.3.0"
 
-[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
-dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] }
+[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
+dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] }
 
 [target.x86_64-fortanix-unknown-sgx.dependencies]
 fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true }
diff --git a/library/std/build.rs b/library/std/build.rs
index ddf6e84d8d0..a81c45609ea 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -37,6 +37,7 @@ fn main() {
         || target.contains("nintendo-3ds")
         || target.contains("vita")
         || target.contains("nto")
+        || target.contains("xous")
         // See src/bootstrap/synthetic_targets.rs
         || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
     {
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index adc1b85fd85..73cce35ac59 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -8,7 +8,7 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 #![deny(unsafe_op_in_unsafe_fn)]
 
-#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx"))))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))]
 mod tests;
 
 use crate::ffi::OsString;
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index 4eb0d92b38b..f63142ff01f 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -511,6 +511,7 @@ impl Error {
     /// let eof_error = Error::from(ErrorKind::UnexpectedEof);
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
+    #[inline(never)]
     pub fn new<E>(kind: ErrorKind, error: E) -> Error
     where
         E: Into<Box<dyn error::Error + Send + Sync>>,
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 55c112c7b80..5e3249655b8 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -260,6 +260,7 @@
     feature(slice_index_methods, coerce_unsized, sgx_platform)
 )]
 #![cfg_attr(windows, feature(round_char_boundary))]
+#![cfg_attr(target_os = "xous", feature(slice_ptr_len))]
 //
 // Language features:
 // tidy-alphabetical-start
@@ -636,9 +637,6 @@ pub mod alloc;
 // Private support modules
 mod panicking;
 
-#[unstable(feature = "ice_to_disk", issue = "none")]
-pub use panicking::panic_hook_with_disk_dump;
-
 #[path = "../../backtrace/src/lib.rs"]
 #[allow(dead_code, unused_attributes, fuzzy_provenance_casts)]
 mod backtrace_rs;
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index 32fd54c8e75..9667d5f920e 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -1,6 +1,6 @@
 #![deny(unsafe_op_in_unsafe_fn)]
 
-#[cfg(all(test, not(target_os = "emscripten")))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_os = "xous"))))]
 mod tests;
 
 use crate::io::prelude::*;
diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs
index 5ca4ed832f3..227e418b709 100644
--- a/library/std/src/net/udp.rs
+++ b/library/std/src/net/udp.rs
@@ -1,4 +1,4 @@
-#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx"))))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))]
 mod tests;
 
 use crate::fmt;
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 634c3cc4a15..de6d784c65b 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -146,6 +146,8 @@ pub mod vita;
 pub mod vxworks;
 #[cfg(target_os = "watchos")]
 pub(crate) mod watchos;
+#[cfg(target_os = "xous")]
+pub mod xous;
 
 #[cfg(any(unix, target_os = "wasi", doc))]
 pub mod fd;
diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs
new file mode 100644
index 00000000000..8be7fbb102f
--- /dev/null
+++ b/library/std/src/os/xous/ffi.rs
@@ -0,0 +1,647 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![stable(feature = "rust1", since = "1.0.0")]
+
+#[path = "../unix/ffi/os_str.rs"]
+mod os_str;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use self::os_str::{OsStrExt, OsStringExt};
+
+mod definitions;
+#[stable(feature = "rust1", since = "1.0.0")]
+pub use definitions::*;
+
+fn lend_mut_impl(
+    connection: Connection,
+    opcode: usize,
+    data: &mut [u8],
+    arg1: usize,
+    arg2: usize,
+    blocking: bool,
+) -> Result<(usize, usize), Error> {
+    let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+    let mut a1: usize = connection.try_into().unwrap();
+    let mut a2 = InvokeType::LendMut as usize;
+    let a3 = opcode;
+    let a4 = data.as_mut_ptr() as usize;
+    let a5 = data.len();
+    let a6 = arg1;
+    let a7 = arg2;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::MemoryReturned as usize {
+        Ok((a1, a2))
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+pub(crate) fn lend_mut(
+    connection: Connection,
+    opcode: usize,
+    data: &mut [u8],
+    arg1: usize,
+    arg2: usize,
+) -> Result<(usize, usize), Error> {
+    lend_mut_impl(connection, opcode, data, arg1, arg2, true)
+}
+
+pub(crate) fn try_lend_mut(
+    connection: Connection,
+    opcode: usize,
+    data: &mut [u8],
+    arg1: usize,
+    arg2: usize,
+) -> Result<(usize, usize), Error> {
+    lend_mut_impl(connection, opcode, data, arg1, arg2, false)
+}
+
+fn lend_impl(
+    connection: Connection,
+    opcode: usize,
+    data: &[u8],
+    arg1: usize,
+    arg2: usize,
+    blocking: bool,
+) -> Result<(usize, usize), Error> {
+    let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+    let a1: usize = connection.try_into().unwrap();
+    let a2 = InvokeType::Lend as usize;
+    let a3 = opcode;
+    let a4 = data.as_ptr() as usize;
+    let a5 = data.len();
+    let mut a6 = arg1;
+    let mut a7 = arg2;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1 => _,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6,
+            inlateout("a7") a7,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::MemoryReturned as usize {
+        Ok((a6, a7))
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+pub(crate) fn lend(
+    connection: Connection,
+    opcode: usize,
+    data: &[u8],
+    arg1: usize,
+    arg2: usize,
+) -> Result<(usize, usize), Error> {
+    lend_impl(connection, opcode, data, arg1, arg2, true)
+}
+
+pub(crate) fn try_lend(
+    connection: Connection,
+    opcode: usize,
+    data: &[u8],
+    arg1: usize,
+    arg2: usize,
+) -> Result<(usize, usize), Error> {
+    lend_impl(connection, opcode, data, arg1, arg2, false)
+}
+
+fn scalar_impl(connection: Connection, args: [usize; 5], blocking: bool) -> Result<(), Error> {
+    let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+    let mut a1: usize = connection.try_into().unwrap();
+    let a2 = InvokeType::Scalar as usize;
+    let a3 = args[0];
+    let a4 = args[1];
+    let a5 = args[2];
+    let a6 = args[3];
+    let a7 = args[4];
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::Ok as usize {
+        Ok(())
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+pub(crate) fn scalar(connection: Connection, args: [usize; 5]) -> Result<(), Error> {
+    scalar_impl(connection, args, true)
+}
+
+pub(crate) fn try_scalar(connection: Connection, args: [usize; 5]) -> Result<(), Error> {
+    scalar_impl(connection, args, false)
+}
+
+fn blocking_scalar_impl(
+    connection: Connection,
+    args: [usize; 5],
+    blocking: bool,
+) -> Result<[usize; 5], Error> {
+    let mut a0 = if blocking { Syscall::SendMessage } else { Syscall::TrySendMessage } as usize;
+    let mut a1: usize = connection.try_into().unwrap();
+    let mut a2 = InvokeType::BlockingScalar as usize;
+    let mut a3 = args[0];
+    let mut a4 = args[1];
+    let mut a5 = args[2];
+    let a6 = args[3];
+    let a7 = args[4];
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2,
+            inlateout("a3") a3,
+            inlateout("a4") a4,
+            inlateout("a5") a5,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::Scalar1 as usize {
+        Ok([a1, 0, 0, 0, 0])
+    } else if result == SyscallResult::Scalar2 as usize {
+        Ok([a1, a2, 0, 0, 0])
+    } else if result == SyscallResult::Scalar5 as usize {
+        Ok([a1, a2, a3, a4, a5])
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+pub(crate) fn blocking_scalar(
+    connection: Connection,
+    args: [usize; 5],
+) -> Result<[usize; 5], Error> {
+    blocking_scalar_impl(connection, args, true)
+}
+
+pub(crate) fn try_blocking_scalar(
+    connection: Connection,
+    args: [usize; 5],
+) -> Result<[usize; 5], Error> {
+    blocking_scalar_impl(connection, args, false)
+}
+
+fn connect_impl(address: ServerAddress, blocking: bool) -> Result<Connection, Error> {
+    let a0 = if blocking { Syscall::Connect } else { Syscall::TryConnect } as usize;
+    let address: [u32; 4] = address.into();
+    let a1: usize = address[0].try_into().unwrap();
+    let a2: usize = address[1].try_into().unwrap();
+    let a3: usize = address[2].try_into().unwrap();
+    let a4: usize = address[3].try_into().unwrap();
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    let mut result: usize;
+    let mut value: usize;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0 => result,
+            inlateout("a1") a1 => value,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+    if result == SyscallResult::ConnectionId as usize {
+        Ok(value.try_into().unwrap())
+    } else if result == SyscallResult::Error as usize {
+        Err(value.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+/// Connect to a Xous server represented by the specified `address`.
+///
+/// The current thread will block until the server is available. Returns
+/// an error if the server cannot accept any more connections.
+pub(crate) fn connect(address: ServerAddress) -> Result<Connection, Error> {
+    connect_impl(address, true)
+}
+
+/// Attempt to connect to a Xous server represented by the specified `address`.
+///
+/// If the server does not exist then None is returned.
+pub(crate) fn try_connect(address: ServerAddress) -> Result<Option<Connection>, Error> {
+    match connect_impl(address, false) {
+        Ok(conn) => Ok(Some(conn)),
+        Err(Error::ServerNotFound) => Ok(None),
+        Err(e) => Err(e),
+    }
+}
+
+/// Terminate the current process and return the specified code to the parent process.
+pub(crate) fn exit(return_code: u32) -> ! {
+    let a0 = Syscall::TerminateProcess as usize;
+    let a1 = return_code as usize;
+    let a2 = 0;
+    let a3 = 0;
+    let a4 = 0;
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            in("a0") a0,
+            in("a1") a1,
+            in("a2") a2,
+            in("a3") a3,
+            in("a4") a4,
+            in("a5") a5,
+            in("a6") a6,
+            in("a7") a7,
+        )
+    };
+    unreachable!();
+}
+
+/// Suspend the current thread and allow another thread to run. This thread may
+/// continue executing again immediately if there are no other threads available
+/// to run on the system.
+pub(crate) fn do_yield() {
+    let a0 = Syscall::Yield as usize;
+    let a1 = 0;
+    let a2 = 0;
+    let a3 = 0;
+    let a4 = 0;
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0 => _,
+            inlateout("a1") a1 => _,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+}
+
+/// Allocate memory from the system. An optional physical and/or virtual address
+/// may be specified in order to ensure memory is allocated at specific offsets,
+/// otherwise the kernel will select an address.
+///
+/// # Safety
+///
+/// This function is safe unless a virtual address is specified. In that case,
+/// the kernel will return an alias to the existing range. This violates Rust's
+/// pointer uniqueness guarantee.
+pub(crate) unsafe fn map_memory<T>(
+    phys: Option<core::ptr::NonNull<T>>,
+    virt: Option<core::ptr::NonNull<T>>,
+    count: usize,
+    flags: MemoryFlags,
+) -> Result<&'static mut [T], Error> {
+    let mut a0 = Syscall::MapMemory as usize;
+    let mut a1 = phys.map(|p| p.as_ptr() as usize).unwrap_or_default();
+    let mut a2 = virt.map(|p| p.as_ptr() as usize).unwrap_or_default();
+    let a3 = count * core::mem::size_of::<T>();
+    let a4 = flags.bits();
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::MemoryRange as usize {
+        let start = core::ptr::from_exposed_addr_mut::<T>(a1);
+        let len = a2 / core::mem::size_of::<T>();
+        let end = unsafe { start.add(len) };
+        Ok(unsafe { core::slice::from_raw_parts_mut(start, len) })
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+/// Destroy the given memory, returning it to the compiler.
+///
+/// Safety: The memory pointed to by `range` should not be used after this
+/// function returns, even if this function returns Err().
+pub(crate) unsafe fn unmap_memory<T>(range: *mut [T]) -> Result<(), Error> {
+    let mut a0 = Syscall::UnmapMemory as usize;
+    let mut a1 = range.as_mut_ptr() as usize;
+    let a2 = range.len();
+    let a3 = 0;
+    let a4 = 0;
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::Ok as usize {
+        Ok(())
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+/// Adjust the memory flags for the given range. This can be used to remove flags
+/// from a given region in order to harden memory access. Note that flags may
+/// only be removed and may never be added.
+///
+/// Safety: The memory pointed to by `range` may become inaccessible or have its
+/// mutability removed. It is up to the caller to ensure that the flags specified
+/// by `new_flags` are upheld, otherwise the program will crash.
+pub(crate) unsafe fn update_memory_flags<T>(
+    range: *mut [T],
+    new_flags: MemoryFlags,
+) -> Result<(), Error> {
+    let mut a0 = Syscall::UpdateMemoryFlags as usize;
+    let mut a1 = range.as_mut_ptr() as usize;
+    let a2 = range.len();
+    let a3 = new_flags.bits();
+    let a4 = 0; // Process ID is currently None
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::Ok as usize {
+        Ok(())
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+/// Create a thread with a given stack and up to four arguments
+pub(crate) fn create_thread(
+    start: *mut usize,
+    stack: *mut [u8],
+    arg0: usize,
+    arg1: usize,
+    arg2: usize,
+    arg3: usize,
+) -> Result<ThreadId, Error> {
+    let mut a0 = Syscall::CreateThread as usize;
+    let mut a1 = start as usize;
+    let a2 = stack.as_mut_ptr() as usize;
+    let a3 = stack.len();
+    let a4 = arg0;
+    let a5 = arg1;
+    let a6 = arg2;
+    let a7 = arg3;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::ThreadId as usize {
+        Ok(a1.into())
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+/// Wait for the given thread to terminate and return the exit code from that thread.
+pub(crate) fn join_thread(thread_id: ThreadId) -> Result<usize, Error> {
+    let mut a0 = Syscall::JoinThread as usize;
+    let mut a1 = thread_id.into();
+    let a2 = 0;
+    let a3 = 0;
+    let a4 = 0;
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::Scalar1 as usize {
+        Ok(a1)
+    } else if result == SyscallResult::Scalar2 as usize {
+        Ok(a1)
+    } else if result == SyscallResult::Scalar5 as usize {
+        Ok(a1)
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+/// Get the current thread's ID
+pub(crate) fn thread_id() -> Result<ThreadId, Error> {
+    let mut a0 = Syscall::GetThreadId as usize;
+    let mut a1 = 0;
+    let a2 = 0;
+    let a3 = 0;
+    let a4 = 0;
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::ThreadId as usize {
+        Ok(a1.into())
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
+
+/// Adjust the given `knob` limit to match the new value `new`. The current value must
+/// match the `current` in order for this to take effect.
+///
+/// The new value is returned as a result of this call. If the call fails, then the old
+/// value is returned. In either case, this function returns successfully.
+///
+/// An error is generated if the `knob` is not a valid limit, or if the call
+/// would not succeed.
+pub(crate) fn adjust_limit(knob: Limits, current: usize, new: usize) -> Result<usize, Error> {
+    let mut a0 = Syscall::JoinThread as usize;
+    let mut a1 = knob as usize;
+    let a2 = current;
+    let a3 = new;
+    let a4 = 0;
+    let a5 = 0;
+    let a6 = 0;
+    let a7 = 0;
+
+    unsafe {
+        core::arch::asm!(
+            "ecall",
+            inlateout("a0") a0,
+            inlateout("a1") a1,
+            inlateout("a2") a2 => _,
+            inlateout("a3") a3 => _,
+            inlateout("a4") a4 => _,
+            inlateout("a5") a5 => _,
+            inlateout("a6") a6 => _,
+            inlateout("a7") a7 => _,
+        )
+    };
+
+    let result = a0;
+
+    if result == SyscallResult::Scalar2 as usize && a1 == knob as usize {
+        Ok(a2)
+    } else if result == SyscallResult::Scalar5 as usize && a1 == knob as usize {
+        Ok(a1)
+    } else if result == SyscallResult::Error as usize {
+        Err(a1.into())
+    } else {
+        Err(Error::InternalError)
+    }
+}
diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs
new file mode 100644
index 00000000000..345005bcc78
--- /dev/null
+++ b/library/std/src/os/xous/ffi/definitions.rs
@@ -0,0 +1,283 @@
+mod memoryflags;
+pub(crate) use memoryflags::*;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+/// Indicates a particular syscall number as used by the Xous kernel.
+#[derive(Copy, Clone)]
+#[repr(usize)]
+pub enum Syscall {
+    MapMemory = 2,
+    Yield = 3,
+    UpdateMemoryFlags = 12,
+    ReceiveMessage = 15,
+    SendMessage = 16,
+    Connect = 17,
+    CreateThread = 18,
+    UnmapMemory = 19,
+    ReturnMemory = 20,
+    TerminateProcess = 22,
+    TrySendMessage = 24,
+    TryConnect = 25,
+    GetThreadId = 32,
+    JoinThread = 36,
+    AdjustProcessLimit = 38,
+    ReturnScalar = 40,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+/// Copies of these invocation types here for when we're running
+/// in environments without libxous.
+#[derive(Copy, Clone)]
+#[repr(usize)]
+pub enum SyscallResult {
+    Ok = 0,
+    Error = 1,
+    MemoryRange = 3,
+    ConnectionId = 7,
+    Message = 9,
+    ThreadId = 10,
+    Scalar1 = 14,
+    Scalar2 = 15,
+    MemoryReturned = 18,
+    Scalar5 = 20,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Copy, Clone)]
+/// A list of all known errors that may be returned by the Xous kernel.
+#[repr(usize)]
+pub enum Error {
+    NoError = 0,
+    BadAlignment = 1,
+    BadAddress = 2,
+    OutOfMemory = 3,
+    MemoryInUse = 4,
+    InterruptNotFound = 5,
+    InterruptInUse = 6,
+    InvalidString = 7,
+    ServerExists = 8,
+    ServerNotFound = 9,
+    ProcessNotFound = 10,
+    ProcessNotChild = 11,
+    ProcessTerminated = 12,
+    Timeout = 13,
+    InternalError = 14,
+    ServerQueueFull = 15,
+    ThreadNotAvailable = 16,
+    UnhandledSyscall = 17,
+    InvalidSyscall = 18,
+    ShareViolation = 19,
+    InvalidThread = 20,
+    InvalidPid = 21,
+    UnknownError = 22,
+    AccessDenied = 23,
+    UseBeforeInit = 24,
+    DoubleFree = 25,
+    DebugInProgress = 26,
+    InvalidLimit = 27,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl From<usize> for Error {
+    fn from(src: usize) -> Self {
+        match src {
+            0 => Self::NoError,
+            1 => Self::BadAlignment,
+            2 => Self::BadAddress,
+            3 => Self::OutOfMemory,
+            4 => Self::MemoryInUse,
+            5 => Self::InterruptNotFound,
+            6 => Self::InterruptInUse,
+            7 => Self::InvalidString,
+            8 => Self::ServerExists,
+            9 => Self::ServerNotFound,
+            10 => Self::ProcessNotFound,
+            11 => Self::ProcessNotChild,
+            12 => Self::ProcessTerminated,
+            13 => Self::Timeout,
+            14 => Self::InternalError,
+            15 => Self::ServerQueueFull,
+            16 => Self::ThreadNotAvailable,
+            17 => Self::UnhandledSyscall,
+            18 => Self::InvalidSyscall,
+            19 => Self::ShareViolation,
+            20 => Self::InvalidThread,
+            21 => Self::InvalidPid,
+            23 => Self::AccessDenied,
+            24 => Self::UseBeforeInit,
+            25 => Self::DoubleFree,
+            26 => Self::DebugInProgress,
+            27 => Self::InvalidLimit,
+            22 | _ => Self::UnknownError,
+        }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl From<i32> for Error {
+    fn from(src: i32) -> Self {
+        let Ok(src) = core::convert::TryInto::<usize>::try_into(src) else {
+            return Self::UnknownError;
+        };
+        src.into()
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Display for Error {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(
+            f,
+            "{}",
+            match self {
+                Error::NoError => "no error occurred",
+                Error::BadAlignment => "memory was not properly aligned",
+                Error::BadAddress => "an invalid address was supplied",
+                Error::OutOfMemory => "the process or service has run out of memory",
+                Error::MemoryInUse => "the requested address is in use",
+                Error::InterruptNotFound =>
+                    "the requested interrupt does not exist on this platform",
+                Error::InterruptInUse => "the requested interrupt is currently in use",
+                Error::InvalidString => "the specified string was not formatted correctly",
+                Error::ServerExists => "a server with that address already exists",
+                Error::ServerNotFound => "the requetsed server could not be found",
+                Error::ProcessNotFound => "the target process does not exist",
+                Error::ProcessNotChild =>
+                    "the requested operation can only be done on child processes",
+                Error::ProcessTerminated => "the target process has crashed",
+                Error::Timeout => "the requested operation timed out",
+                Error::InternalError => "an internal error occurred",
+                Error::ServerQueueFull => "the server has too many pending messages",
+                Error::ThreadNotAvailable => "the specified thread does not exist",
+                Error::UnhandledSyscall => "the kernel did not recognize that syscall",
+                Error::InvalidSyscall => "the syscall had incorrect parameters",
+                Error::ShareViolation => "an attempt was made to share memory twice",
+                Error::InvalidThread => "tried to resume a thread that was not ready",
+                Error::InvalidPid => "kernel attempted to use a pid that was not valid",
+                Error::AccessDenied => "no permission to perform the requested operation",
+                Error::UseBeforeInit => "attempt to use a service before initialization finished",
+                Error::DoubleFree => "the requested resource was freed twice",
+                Error::DebugInProgress => "kernel attempted to activate a thread being debugged",
+                Error::InvalidLimit => "process attempted to adjust an invalid limit",
+                Error::UnknownError => "an unknown error occurred",
+            }
+        )
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Debug for Error {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(f, "{}", self)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl crate::error::Error for Error {}
+
+/// Indicates the type of Message that is sent when making a `SendMessage` syscall.
+#[derive(Copy, Clone)]
+#[repr(usize)]
+pub(crate) enum InvokeType {
+    LendMut = 1,
+    Lend = 2,
+    Move = 3,
+    Scalar = 4,
+    BlockingScalar = 5,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Debug, Copy, Clone)]
+/// A representation of a connection to a Xous service.
+pub struct Connection(u32);
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl From<u32> for Connection {
+    fn from(src: u32) -> Connection {
+        Connection(src)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl TryFrom<usize> for Connection {
+    type Error = core::num::TryFromIntError;
+    fn try_from(src: usize) -> Result<Self, Self::Error> {
+        Ok(Connection(src.try_into()?))
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Into<u32> for Connection {
+    fn into(self) -> u32 {
+        self.0
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl TryInto<usize> for Connection {
+    type Error = core::num::TryFromIntError;
+    fn try_into(self) -> Result<usize, Self::Error> {
+        self.0.try_into()
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Debug)]
+pub enum ServerAddressError {
+    InvalidLength,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct ServerAddress([u32; 4]);
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl TryFrom<&str> for ServerAddress {
+    type Error = ServerAddressError;
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        let b = value.as_bytes();
+        if b.len() == 0 || b.len() > 16 {
+            return Err(Self::Error::InvalidLength);
+        }
+
+        let mut this_temp = [0u8; 16];
+        for (dest, src) in this_temp.iter_mut().zip(b.iter()) {
+            *dest = *src;
+        }
+
+        let mut this = [0u32; 4];
+        for (dest, src) in this.iter_mut().zip(this_temp.chunks_exact(4)) {
+            *dest = u32::from_le_bytes(src.try_into().unwrap());
+        }
+        Ok(ServerAddress(this))
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Into<[u32; 4]> for ServerAddress {
+    fn into(self) -> [u32; 4] {
+        self.0
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub(crate) struct ThreadId(usize);
+
+impl From<usize> for ThreadId {
+    fn from(src: usize) -> ThreadId {
+        ThreadId(src)
+    }
+}
+
+impl Into<usize> for ThreadId {
+    fn into(self) -> usize {
+        self.0
+    }
+}
+
+#[derive(Copy, Clone)]
+#[repr(usize)]
+/// Limits that can be passed to `AdjustLimit`
+pub(crate) enum Limits {
+    HeapMaximum = 1,
+    HeapSize = 2,
+}
diff --git a/library/std/src/os/xous/ffi/definitions/memoryflags.rs b/library/std/src/os/xous/ffi/definitions/memoryflags.rs
new file mode 100644
index 00000000000..af9de3cbff2
--- /dev/null
+++ b/library/std/src/os/xous/ffi/definitions/memoryflags.rs
@@ -0,0 +1,176 @@
+/// Flags to be passed to the MapMemory struct.
+/// Note that it is an error to have memory be
+/// writable and not readable.
+#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Debug)]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct MemoryFlags {
+    bits: usize,
+}
+
+impl MemoryFlags {
+    /// Free this memory
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub const FREE: Self = Self { bits: 0b0000_0000 };
+
+    /// Immediately allocate this memory.  Otherwise it will
+    /// be demand-paged.  This is implicitly set when `phys`
+    /// is not 0.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub const RESERVE: Self = Self { bits: 0b0000_0001 };
+
+    /// Allow the CPU to read from this page.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub const R: Self = Self { bits: 0b0000_0010 };
+
+    /// Allow the CPU to write to this page.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub const W: Self = Self { bits: 0b0000_0100 };
+
+    /// Allow the CPU to execute from this page.
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub const X: Self = Self { bits: 0b0000_1000 };
+
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn bits(&self) -> usize {
+        self.bits
+    }
+
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn from_bits(raw: usize) -> Option<MemoryFlags> {
+        if raw > 16 { None } else { Some(MemoryFlags { bits: raw }) }
+    }
+
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn is_empty(&self) -> bool {
+        self.bits == 0
+    }
+
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn empty() -> MemoryFlags {
+        MemoryFlags { bits: 0 }
+    }
+
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub fn all() -> MemoryFlags {
+        MemoryFlags { bits: 15 }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Binary for MemoryFlags {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        core::fmt::Binary::fmt(&self.bits, f)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::Octal for MemoryFlags {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        core::fmt::Octal::fmt(&self.bits, f)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::LowerHex for MemoryFlags {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        core::fmt::LowerHex::fmt(&self.bits, f)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::fmt::UpperHex for MemoryFlags {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        core::fmt::UpperHex::fmt(&self.bits, f)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitOr for MemoryFlags {
+    type Output = Self;
+
+    /// Returns the union of the two sets of flags.
+    #[inline]
+    fn bitor(self, other: MemoryFlags) -> Self {
+        Self { bits: self.bits | other.bits }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitOrAssign for MemoryFlags {
+    /// Adds the set of flags.
+    #[inline]
+    fn bitor_assign(&mut self, other: Self) {
+        self.bits |= other.bits;
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitXor for MemoryFlags {
+    type Output = Self;
+
+    /// Returns the left flags, but with all the right flags toggled.
+    #[inline]
+    fn bitxor(self, other: Self) -> Self {
+        Self { bits: self.bits ^ other.bits }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitXorAssign for MemoryFlags {
+    /// Toggles the set of flags.
+    #[inline]
+    fn bitxor_assign(&mut self, other: Self) {
+        self.bits ^= other.bits;
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitAnd for MemoryFlags {
+    type Output = Self;
+
+    /// Returns the intersection between the two sets of flags.
+    #[inline]
+    fn bitand(self, other: Self) -> Self {
+        Self { bits: self.bits & other.bits }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::BitAndAssign for MemoryFlags {
+    /// Disables all flags disabled in the set.
+    #[inline]
+    fn bitand_assign(&mut self, other: Self) {
+        self.bits &= other.bits;
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::Sub for MemoryFlags {
+    type Output = Self;
+
+    /// Returns the set difference of the two sets of flags.
+    #[inline]
+    fn sub(self, other: Self) -> Self {
+        Self { bits: self.bits & !other.bits }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::SubAssign for MemoryFlags {
+    /// Disables all flags enabled in the set.
+    #[inline]
+    fn sub_assign(&mut self, other: Self) {
+        self.bits &= !other.bits;
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl core::ops::Not for MemoryFlags {
+    type Output = Self;
+
+    /// Returns the complement of this set of flags.
+    #[inline]
+    fn not(self) -> Self {
+        Self { bits: !self.bits } & MemoryFlags { bits: 15 }
+    }
+}
diff --git a/library/std/src/os/xous/mod.rs b/library/std/src/os/xous/mod.rs
new file mode 100644
index 00000000000..153694a89a7
--- /dev/null
+++ b/library/std/src/os/xous/mod.rs
@@ -0,0 +1,17 @@
+#![stable(feature = "rust1", since = "1.0.0")]
+#![doc(cfg(target_os = "xous"))]
+
+pub mod ffi;
+
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod services;
+
+/// A prelude for conveniently writing platform-specific code.
+///
+/// Includes all extension traits, and some important type definitions.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub mod prelude {
+    #[doc(no_inline)]
+    #[stable(feature = "rust1", since = "1.0.0")]
+    pub use super::ffi::{OsStrExt, OsStringExt};
+}
diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs
new file mode 100644
index 00000000000..5c219f1fbb9
--- /dev/null
+++ b/library/std/src/os/xous/services.rs
@@ -0,0 +1,132 @@
+use crate::os::xous::ffi::Connection;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+mod log;
+pub(crate) use log::*;
+
+mod systime;
+pub(crate) use systime::*;
+
+mod ticktimer;
+pub(crate) use ticktimer::*;
+
+mod ns {
+    const NAME_MAX_LENGTH: usize = 64;
+    use crate::os::xous::ffi::{lend_mut, Connection};
+    // By making this repr(C), the layout of this struct becomes well-defined
+    // and no longer shifts around.
+    // By marking it as `align(4096)` we define that it will be page-aligned,
+    // meaning it can be sent between processes. We make sure to pad out the
+    // entire struct so that memory isn't leaked to the name server.
+    #[repr(C, align(4096))]
+    struct ConnectRequest {
+        data: [u8; 4096],
+    }
+
+    impl ConnectRequest {
+        pub fn new(name: &str) -> Self {
+            let mut cr = ConnectRequest { data: [0u8; 4096] };
+            let name_bytes = name.as_bytes();
+
+            // Copy the string into our backing store.
+            for (&src_byte, dest_byte) in name_bytes.iter().zip(&mut cr.data[0..NAME_MAX_LENGTH]) {
+                *dest_byte = src_byte;
+            }
+
+            // Set the string length to the length of the passed-in String,
+            // or the maximum possible length. Which ever is smaller.
+            for (&src_byte, dest_byte) in (name.len().min(NAME_MAX_LENGTH) as u32)
+                .to_le_bytes()
+                .iter()
+                .zip(&mut cr.data[NAME_MAX_LENGTH..])
+            {
+                *dest_byte = src_byte;
+            }
+            cr
+        }
+    }
+
+    pub fn connect_with_name_impl(name: &str, blocking: bool) -> Option<Connection> {
+        let mut request = ConnectRequest::new(name);
+        let opcode = if blocking {
+            6 /* BlockingConnect */
+        } else {
+            7 /* TryConnect */
+        };
+        let cid = if blocking { super::name_server() } else { super::try_name_server()? };
+
+        lend_mut(cid, opcode, &mut request.data, 0, name.len().min(NAME_MAX_LENGTH))
+            .expect("unable to perform lookup");
+
+        // Read the result code back from the nameserver
+        let result = u32::from_le_bytes(request.data[0..4].try_into().unwrap());
+        if result == 0 {
+            // If the result was successful, then the CID is stored in the next 4 bytes
+            Some(u32::from_le_bytes(request.data[4..8].try_into().unwrap()).into())
+        } else {
+            None
+        }
+    }
+
+    pub fn connect_with_name(name: &str) -> Option<Connection> {
+        connect_with_name_impl(name, true)
+    }
+
+    pub fn try_connect_with_name(name: &str) -> Option<Connection> {
+        connect_with_name_impl(name, false)
+    }
+}
+
+/// Attempt to connect to a server by name. If the server does not exist, this will
+/// block until the server is created.
+///
+/// Note that this is different from connecting to a server by address. Server
+/// addresses are always 16 bytes long, whereas server names are arbitrary-length
+/// strings up to 64 bytes in length.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn connect(name: &str) -> Option<Connection> {
+    ns::connect_with_name(name)
+}
+
+/// Attempt to connect to a server by name. If the server does not exist, this will
+/// immediately return `None`.
+///
+/// Note that this is different from connecting to a server by address. Server
+/// addresses are always 16 bytes long, whereas server names are arbitrary-length
+/// strings.
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn try_connect(name: &str) -> Option<Connection> {
+    ns::try_connect_with_name(name)
+}
+
+static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+
+/// Return a `Connection` to the name server. If the name server has not been started,
+/// then this call will block until the name server has been started. The `Connection`
+/// will be shared among all connections in a process, so it is safe to call this
+/// multiple times.
+pub(crate) fn name_server() -> Connection {
+    let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
+    if cid != 0 {
+        return cid.into();
+    }
+
+    let cid = crate::os::xous::ffi::connect("xous-name-server".try_into().unwrap()).unwrap();
+    NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+    cid
+}
+
+fn try_name_server() -> Option<Connection> {
+    let cid = NAME_SERVER_CONNECTION.load(Ordering::Relaxed);
+    if cid != 0 {
+        return Some(cid.into());
+    }
+
+    if let Ok(Some(cid)) = crate::os::xous::ffi::try_connect("xous-name-server".try_into().unwrap())
+    {
+        NAME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+        Some(cid)
+    } else {
+        None
+    }
+}
diff --git a/library/std/src/os/xous/services/log.rs b/library/std/src/os/xous/services/log.rs
new file mode 100644
index 00000000000..e6bae929eac
--- /dev/null
+++ b/library/std/src/os/xous/services/log.rs
@@ -0,0 +1,63 @@
+use crate::os::xous::ffi::Connection;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+/// Group `usize` bytes into a `usize` and return it, beginning
+/// from `offset` * sizeof(usize) bytes from the start. For example,
+/// `group_or_null([1,2,3,4,5,6,7,8], 1)` on a 32-bit system will
+/// return a usize with 5678 packed into it.
+fn group_or_null(data: &[u8], offset: usize) -> usize {
+    let start = offset * core::mem::size_of::<usize>();
+    let mut out_array = [0u8; core::mem::size_of::<usize>()];
+    if start < data.len() {
+        for (dest, src) in out_array.iter_mut().zip(&data[start..]) {
+            *dest = *src;
+        }
+    }
+    usize::from_le_bytes(out_array)
+}
+
+pub(crate) enum LogScalar<'a> {
+    /// A panic occurred, and a panic log is forthcoming
+    BeginPanic,
+
+    /// Some number of bytes will be appended to the log message
+    AppendPanicMessage(&'a [u8]),
+}
+
+impl<'a> Into<[usize; 5]> for LogScalar<'a> {
+    fn into(self) -> [usize; 5] {
+        match self {
+            LogScalar::BeginPanic => [1000, 0, 0, 0, 0],
+            LogScalar::AppendPanicMessage(c) =>
+            // Text is grouped into 4x `usize` words. The id is 1100 plus
+            // the number of characters in this message.
+            // Ignore errors since we're already panicking.
+            {
+                [
+                    1100 + c.len(),
+                    group_or_null(&c, 0),
+                    group_or_null(&c, 1),
+                    group_or_null(&c, 2),
+                    group_or_null(&c, 3),
+                ]
+            }
+        }
+    }
+}
+
+/// Return a `Connection` to the log server, which is used for printing messages to
+/// the console and reporting panics. If the log server has not yet started, this
+/// will block until the server is running. It is safe to call this multiple times,
+/// because the address is shared among all threads in a process.
+pub(crate) fn log_server() -> Connection {
+    static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+
+    let cid = LOG_SERVER_CONNECTION.load(Ordering::Relaxed);
+    if cid != 0 {
+        return cid.into();
+    }
+
+    let cid = crate::os::xous::ffi::connect("xous-log-server ".try_into().unwrap()).unwrap();
+    LOG_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+    cid
+}
diff --git a/library/std/src/os/xous/services/systime.rs b/library/std/src/os/xous/services/systime.rs
new file mode 100644
index 00000000000..bbb875c6942
--- /dev/null
+++ b/library/std/src/os/xous/services/systime.rs
@@ -0,0 +1,28 @@
+use crate::os::xous::ffi::{connect, Connection};
+use core::sync::atomic::{AtomicU32, Ordering};
+
+pub(crate) enum SystimeScalar {
+    GetUtcTimeMs,
+}
+
+impl Into<[usize; 5]> for SystimeScalar {
+    fn into(self) -> [usize; 5] {
+        match self {
+            SystimeScalar::GetUtcTimeMs => [3, 0, 0, 0, 0],
+        }
+    }
+}
+
+/// Return a `Connection` to the systime server. This server is used for reporting the
+/// realtime clock.
+pub(crate) fn systime_server() -> Connection {
+    static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+    let cid = SYSTIME_SERVER_CONNECTION.load(Ordering::Relaxed);
+    if cid != 0 {
+        return cid.into();
+    }
+
+    let cid = connect("timeserverpublic".try_into().unwrap()).unwrap();
+    SYSTIME_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+    cid
+}
diff --git a/library/std/src/os/xous/services/ticktimer.rs b/library/std/src/os/xous/services/ticktimer.rs
new file mode 100644
index 00000000000..7759303fdbe
--- /dev/null
+++ b/library/std/src/os/xous/services/ticktimer.rs
@@ -0,0 +1,42 @@
+use crate::os::xous::ffi::Connection;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+pub(crate) enum TicktimerScalar {
+    ElapsedMs,
+    SleepMs(usize),
+    LockMutex(usize /* cookie */),
+    UnlockMutex(usize /* cookie */),
+    WaitForCondition(usize /* cookie */, usize /* timeout (ms) */),
+    NotifyCondition(usize /* cookie */, usize /* count */),
+    FreeMutex(usize /* cookie */),
+    FreeCondition(usize /* cookie */),
+}
+
+impl Into<[usize; 5]> for TicktimerScalar {
+    fn into(self) -> [usize; 5] {
+        match self {
+            TicktimerScalar::ElapsedMs => [0, 0, 0, 0, 0],
+            TicktimerScalar::SleepMs(msecs) => [1, msecs, 0, 0, 0],
+            TicktimerScalar::LockMutex(cookie) => [6, cookie, 0, 0, 0],
+            TicktimerScalar::UnlockMutex(cookie) => [7, cookie, 0, 0, 0],
+            TicktimerScalar::WaitForCondition(cookie, timeout_ms) => [8, cookie, timeout_ms, 0, 0],
+            TicktimerScalar::NotifyCondition(cookie, count) => [9, cookie, count, 0, 0],
+            TicktimerScalar::FreeMutex(cookie) => [10, cookie, 0, 0, 0],
+            TicktimerScalar::FreeCondition(cookie) => [11, cookie, 0, 0, 0],
+        }
+    }
+}
+
+/// Return a `Connection` to the ticktimer server. This server is used for synchronization
+/// primitives such as sleep, Mutex, and Condvar.
+pub(crate) fn ticktimer_server() -> Connection {
+    static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0);
+    let cid = TICKTIMER_SERVER_CONNECTION.load(Ordering::Relaxed);
+    if cid != 0 {
+        return cid.into();
+    }
+
+    let cid = crate::os::xous::ffi::connect("ticktimer-server".try_into().unwrap()).unwrap();
+    TICKTIMER_SERVER_CONNECTION.store(cid.into(), Ordering::Relaxed);
+    cid
+}
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index b20e5464e6e..d7a2baa1ff5 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -10,7 +10,7 @@
 #![deny(unsafe_op_in_unsafe_fn)]
 
 use crate::panic::BacktraceStyle;
-use core::panic::{BoxMeUp, Location, PanicInfo};
+use core::panic::{Location, PanicInfo, PanicPayload};
 
 use crate::any::Any;
 use crate::fmt;
@@ -47,9 +47,9 @@ extern "C" {
 }
 
 extern "Rust" {
-    /// `BoxMeUp` lazily performs allocation only when needed (this avoids
+    /// `PanicPayload` lazily performs allocation only when needed (this avoids
     /// allocations when using the "abort" panic runtime).
-    fn __rust_start_panic(payload: &mut dyn BoxMeUp) -> u32;
+    fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32;
 }
 
 /// This function is called by the panic runtime if FFI code catches a Rust
@@ -236,14 +236,6 @@ where
 
 /// The default panic handler.
 fn default_hook(info: &PanicInfo<'_>) {
-    panic_hook_with_disk_dump(info, None)
-}
-
-#[unstable(feature = "ice_to_disk", issue = "none")]
-/// The implementation of the default panic handler.
-///
-/// It can also write the backtrace to a given `path`. This functionality is used only by `rustc`.
-pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path::Path>) {
     // If this is a double panic, make sure that we print a backtrace
     // for this panic. Otherwise only print it if logging is enabled.
     let backtrace = if info.force_no_backtrace() {
@@ -267,7 +259,7 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
     let thread = thread_info::current_thread();
     let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
 
-    let write = |err: &mut dyn crate::io::Write, backtrace: Option<BacktraceStyle>| {
+    let write = |err: &mut dyn crate::io::Write| {
         let _ = writeln!(err, "thread '{name}' panicked at {location}:\n{msg}");
 
         static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
@@ -281,19 +273,11 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
             }
             Some(BacktraceStyle::Off) => {
                 if FIRST_PANIC.swap(false, Ordering::SeqCst) {
-                    if let Some(path) = path {
-                        let _ = writeln!(
-                            err,
-                            "note: a backtrace for this error was stored at `{}`",
-                            path.display(),
-                        );
-                    } else {
-                        let _ = writeln!(
-                            err,
-                            "note: run with `RUST_BACKTRACE=1` environment variable to display a \
+                    let _ = writeln!(
+                        err,
+                        "note: run with `RUST_BACKTRACE=1` environment variable to display a \
                              backtrace"
-                        );
-                    }
+                    );
                 }
             }
             // If backtraces aren't supported or are forced-off, do nothing.
@@ -301,17 +285,11 @@ pub fn panic_hook_with_disk_dump(info: &PanicInfo<'_>, path: Option<&crate::path
         }
     };
 
-    if let Some(path) = path
-        && let Ok(mut out) = crate::fs::File::options().create(true).append(true).open(&path)
-    {
-        write(&mut out, BacktraceStyle::full());
-    }
-
     if let Some(local) = set_output_capture(None) {
-        write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()), backtrace);
+        write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()));
         set_output_capture(Some(local));
     } else if let Some(mut out) = panic_output() {
-        write(&mut out, backtrace);
+        write(&mut out);
     }
 }
 
@@ -565,14 +543,14 @@ pub fn panicking() -> bool {
 #[cfg(not(test))]
 #[panic_handler]
 pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
-    struct PanicPayload<'a> {
+    struct FormatStringPayload<'a> {
         inner: &'a fmt::Arguments<'a>,
         string: Option<String>,
     }
 
-    impl<'a> PanicPayload<'a> {
-        fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
-            PanicPayload { inner, string: None }
+    impl<'a> FormatStringPayload<'a> {
+        fn new(inner: &'a fmt::Arguments<'a>) -> Self {
+            Self { inner, string: None }
         }
 
         fn fill(&mut self) -> &mut String {
@@ -588,7 +566,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
         }
     }
 
-    unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
+    unsafe impl<'a> PanicPayload for FormatStringPayload<'a> {
         fn take_box(&mut self) -> *mut (dyn Any + Send) {
             // We do two allocations here, unfortunately. But (a) they're required with the current
             // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in
@@ -602,9 +580,9 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
         }
     }
 
-    struct StrPanicPayload(&'static str);
+    struct StaticStrPayload(&'static str);
 
-    unsafe impl BoxMeUp for StrPanicPayload {
+    unsafe impl PanicPayload for StaticStrPayload {
         fn take_box(&mut self) -> *mut (dyn Any + Send) {
             Box::into_raw(Box::new(self.0))
         }
@@ -621,7 +599,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
         // `rust_panic_with_hook` construct a new `PanicInfo`?
         if let Some(msg) = msg.as_str() {
             rust_panic_with_hook(
-                &mut StrPanicPayload(msg),
+                &mut StaticStrPayload(msg),
                 info.message(),
                 loc,
                 info.can_unwind(),
@@ -629,7 +607,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
             );
         } else {
             rust_panic_with_hook(
-                &mut PanicPayload::new(msg),
+                &mut FormatStringPayload::new(msg),
                 info.message(),
                 loc,
                 info.can_unwind(),
@@ -659,7 +637,7 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
     let loc = Location::caller();
     return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
         rust_panic_with_hook(
-            &mut PanicPayload::new(msg),
+            &mut Payload::new(msg),
             None,
             loc,
             /* can_unwind */ true,
@@ -667,17 +645,17 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
         )
     });
 
-    struct PanicPayload<A> {
+    struct Payload<A> {
         inner: Option<A>,
     }
 
-    impl<A: Send + 'static> PanicPayload<A> {
-        fn new(inner: A) -> PanicPayload<A> {
-            PanicPayload { inner: Some(inner) }
+    impl<A: Send + 'static> Payload<A> {
+        fn new(inner: A) -> Payload<A> {
+            Payload { inner: Some(inner) }
         }
     }
 
-    unsafe impl<A: Send + 'static> BoxMeUp for PanicPayload<A> {
+    unsafe impl<A: Send + 'static> PanicPayload for Payload<A> {
         fn take_box(&mut self) -> *mut (dyn Any + Send) {
             // Note that this should be the only allocation performed in this code path. Currently
             // this means that panic!() on OOM will invoke this code path, but then again we're not
@@ -706,7 +684,7 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
 /// panics, panic hooks, and finally dispatching to the panic runtime to either
 /// abort or unwind.
 fn rust_panic_with_hook(
-    payload: &mut dyn BoxMeUp,
+    payload: &mut dyn PanicPayload,
     message: Option<&fmt::Arguments<'_>>,
     location: &Location<'_>,
     can_unwind: bool,
@@ -782,7 +760,7 @@ pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
 
     struct RewrapBox(Box<dyn Any + Send>);
 
-    unsafe impl BoxMeUp for RewrapBox {
+    unsafe impl PanicPayload for RewrapBox {
         fn take_box(&mut self) -> *mut (dyn Any + Send) {
             Box::into_raw(mem::replace(&mut self.0, Box::new(())))
         }
@@ -799,7 +777,7 @@ pub fn rust_panic_without_hook(payload: Box<dyn Any + Send>) -> ! {
 /// yer breakpoints.
 #[inline(never)]
 #[cfg_attr(not(test), rustc_std_internal_symbol)]
-fn rust_panic(msg: &mut dyn BoxMeUp) -> ! {
+fn rust_panic(msg: &mut dyn PanicPayload) -> ! {
     let code = unsafe { __rust_start_panic(msg) };
     rtabort!("failed to initiate panic, error {code}")
 }
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 762a1e9b408..5df1105e264 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -101,7 +101,7 @@
 #![stable(feature = "process", since = "1.0.0")]
 #![deny(unsafe_op_in_unsafe_fn)]
 
-#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx"))))]
+#[cfg(all(test, not(any(target_os = "emscripten", target_env = "sgx", target_os = "xous"))))]
 mod tests;
 
 use crate::io::prelude::*;
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index 63bd783746b..457eb782ccc 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -44,6 +44,9 @@ cfg_if::cfg_if! {
     } else if #[cfg(target_family = "wasm")] {
         mod wasm;
         pub use self::wasm::*;
+    } else if #[cfg(target_os = "xous")] {
+        mod xous;
+        pub use self::xous::*;
     } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
         mod sgx;
         pub use self::sgx::*;
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 7c242d4d334..2afec897a88 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -324,8 +324,10 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
                     if libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) == 0 {
                         let count = libc::CPU_COUNT(&set) as usize;
                         let count = count.min(quota);
-                        // SAFETY: affinity mask can't be empty and the quota gets clamped to a minimum of 1
-                        return Ok(NonZeroUsize::new_unchecked(count));
+                        // reported to occur on MIPS kernels older than our minimum supported kernel version for those targets
+                        let count = NonZeroUsize::new(count)
+                            .expect("CPU count must be > 0. This may be a bug in sched_getaffinity(); try upgrading the kernel.");
+                        return Ok(count);
                     }
                 }
             }
diff --git a/library/std/src/sys/xous/alloc.rs b/library/std/src/sys/xous/alloc.rs
new file mode 100644
index 00000000000..b3a3e691e0d
--- /dev/null
+++ b/library/std/src/sys/xous/alloc.rs
@@ -0,0 +1,62 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+
+static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new();
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+    #[inline]
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+        // Calling malloc() is safe because preconditions on this function match the trait method preconditions.
+        let _lock = lock::lock();
+        unsafe { DLMALLOC.malloc(layout.size(), layout.align()) }
+    }
+
+    #[inline]
+    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+        // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+        // Calling calloc() is safe because preconditions on this function match the trait method preconditions.
+        let _lock = lock::lock();
+        unsafe { DLMALLOC.calloc(layout.size(), layout.align()) }
+    }
+
+    #[inline]
+    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+        // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+        // Calling free() is safe because preconditions on this function match the trait method preconditions.
+        let _lock = lock::lock();
+        unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) }
+    }
+
+    #[inline]
+    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+        // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access.
+        // Calling realloc() is safe because preconditions on this function match the trait method preconditions.
+        let _lock = lock::lock();
+        unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) }
+    }
+}
+
+mod lock {
+    use crate::sync::atomic::{AtomicI32, Ordering::SeqCst};
+
+    static LOCKED: AtomicI32 = AtomicI32::new(0);
+
+    pub struct DropLock;
+
+    pub fn lock() -> DropLock {
+        loop {
+            if LOCKED.swap(1, SeqCst) == 0 {
+                return DropLock;
+            }
+            crate::os::xous::ffi::do_yield();
+        }
+    }
+
+    impl Drop for DropLock {
+        fn drop(&mut self) {
+            let r = LOCKED.swap(0, SeqCst);
+            debug_assert_eq!(r, 1);
+        }
+    }
+}
diff --git a/library/std/src/sys/xous/locks/condvar.rs b/library/std/src/sys/xous/locks/condvar.rs
new file mode 100644
index 00000000000..1bb38dfa341
--- /dev/null
+++ b/library/std/src/sys/xous/locks/condvar.rs
@@ -0,0 +1,111 @@
+use super::mutex::Mutex;
+use crate::os::xous::ffi::{blocking_scalar, scalar};
+use crate::os::xous::services::ticktimer_server;
+use crate::sync::Mutex as StdMutex;
+use crate::time::Duration;
+
+// The implementation is inspired by Andrew D. Birrell's paper
+// "Implementing Condition Variables with Semaphores"
+
+pub struct Condvar {
+    counter: StdMutex<usize>,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Condvar {
+    #[inline]
+    #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
+    pub const fn new() -> Condvar {
+        Condvar { counter: StdMutex::new(0) }
+    }
+
+    pub fn notify_one(&self) {
+        let mut counter = self.counter.lock().unwrap();
+        if *counter <= 0 {
+            return;
+        } else {
+            *counter -= 1;
+        }
+        let result = blocking_scalar(
+            ticktimer_server(),
+            crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), 1).into(),
+        );
+        drop(counter);
+        result.expect("failure to send NotifyCondition command");
+    }
+
+    pub fn notify_all(&self) {
+        let mut counter = self.counter.lock().unwrap();
+        if *counter <= 0 {
+            return;
+        }
+        let result = blocking_scalar(
+            ticktimer_server(),
+            crate::os::xous::services::TicktimerScalar::NotifyCondition(self.index(), *counter)
+                .into(),
+        );
+        *counter = 0;
+        drop(counter);
+
+        result.expect("failure to send NotifyCondition command");
+    }
+
+    fn index(&self) -> usize {
+        self as *const Condvar as usize
+    }
+
+    pub unsafe fn wait(&self, mutex: &Mutex) {
+        let mut counter = self.counter.lock().unwrap();
+        *counter += 1;
+        unsafe { mutex.unlock() };
+        drop(counter);
+
+        let result = blocking_scalar(
+            ticktimer_server(),
+            crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), 0).into(),
+        );
+        unsafe { mutex.lock() };
+
+        result.expect("Ticktimer: failure to send WaitForCondition command");
+    }
+
+    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        let mut counter = self.counter.lock().unwrap();
+        *counter += 1;
+        unsafe { mutex.unlock() };
+        drop(counter);
+
+        let mut millis = dur.as_millis() as usize;
+        if millis == 0 {
+            millis = 1;
+        }
+
+        let result = blocking_scalar(
+            ticktimer_server(),
+            crate::os::xous::services::TicktimerScalar::WaitForCondition(self.index(), millis)
+                .into(),
+        );
+        unsafe { mutex.lock() };
+
+        let result = result.expect("Ticktimer: failure to send WaitForCondition command")[0] == 0;
+
+        // If we awoke due to a timeout, decrement the wake count, as that would not have
+        // been done in the `notify()` call.
+        if !result {
+            *self.counter.lock().unwrap() -= 1;
+        }
+        result
+    }
+}
+
+impl Drop for Condvar {
+    fn drop(&mut self) {
+        scalar(
+            ticktimer_server(),
+            crate::os::xous::services::TicktimerScalar::FreeCondition(self.index()).into(),
+        )
+        .ok();
+    }
+}
diff --git a/library/std/src/sys/xous/locks/mod.rs b/library/std/src/sys/xous/locks/mod.rs
new file mode 100644
index 00000000000..f3c5c5d9fb0
--- /dev/null
+++ b/library/std/src/sys/xous/locks/mod.rs
@@ -0,0 +1,7 @@
+mod condvar;
+mod mutex;
+mod rwlock;
+
+pub use condvar::*;
+pub use mutex::*;
+pub use rwlock::*;
diff --git a/library/std/src/sys/xous/locks/mutex.rs b/library/std/src/sys/xous/locks/mutex.rs
new file mode 100644
index 00000000000..ea51776d54e
--- /dev/null
+++ b/library/std/src/sys/xous/locks/mutex.rs
@@ -0,0 +1,116 @@
+use crate::os::xous::ffi::{blocking_scalar, do_yield, scalar};
+use crate::os::xous::services::ticktimer_server;
+use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst};
+
+pub struct Mutex {
+    /// The "locked" value indicates how many threads are waiting on this
+    /// Mutex. Possible values are:
+    ///     0: The lock is unlocked
+    ///     1: The lock is locked and uncontended
+    ///   >=2: The lock is locked and contended
+    ///
+    /// A lock is "contended" when there is more than one thread waiting
+    /// for a lock, or it is locked for long periods of time. Rather than
+    /// spinning, these locks send a Message to the ticktimer server
+    /// requesting that they be woken up when a lock is unlocked.
+    locked: AtomicUsize,
+
+    /// Whether this Mutex ever was contended, and therefore made a trip
+    /// to the ticktimer server. If this was never set, then we were never
+    /// on the slow path and can skip deregistering the mutex.
+    contended: AtomicBool,
+}
+
+impl Mutex {
+    #[inline]
+    #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
+    pub const fn new() -> Mutex {
+        Mutex { locked: AtomicUsize::new(0), contended: AtomicBool::new(false) }
+    }
+
+    fn index(&self) -> usize {
+        self as *const Mutex as usize
+    }
+
+    #[inline]
+    pub unsafe fn lock(&self) {
+        // Try multiple times to acquire the lock without resorting to the ticktimer
+        // server. For locks that are held for a short amount of time, this will
+        // result in the ticktimer server never getting invoked. The `locked` value
+        // will be either 0 or 1.
+        for _attempts in 0..3 {
+            if unsafe { self.try_lock() } {
+                return;
+            }
+            do_yield();
+        }
+
+        // Try one more time to lock. If the lock is released between the previous code and
+        // here, then the inner `locked` value will be 1 at the end of this. If it was not
+        // locked, then the value will be more than 1, for example if there are multiple other
+        // threads waiting on this lock.
+        if unsafe { self.try_lock_or_poison() } {
+            return;
+        }
+
+        // When this mutex is dropped, we will need to deregister it with the server.
+        self.contended.store(true, Relaxed);
+
+        // The lock is now "contended". When the lock is released, a Message will get sent to the
+        // ticktimer server to wake it up. Note that this may already have happened, so the actual
+        // value of `lock` may be anything (0, 1, 2, ...).
+        blocking_scalar(
+            ticktimer_server(),
+            crate::os::xous::services::TicktimerScalar::LockMutex(self.index()).into(),
+        )
+        .expect("failure to send LockMutex command");
+    }
+
+    #[inline]
+    pub unsafe fn unlock(&self) {
+        let prev = self.locked.fetch_sub(1, SeqCst);
+
+        // If the previous value was 1, then this was a "fast path" unlock, so no
+        // need to involve the Ticktimer server
+        if prev == 1 {
+            return;
+        }
+
+        // If it was 0, then something has gone seriously wrong and the counter
+        // has just wrapped around.
+        if prev == 0 {
+            panic!("mutex lock count underflowed");
+        }
+
+        // Unblock one thread that is waiting on this message.
+        scalar(
+            ticktimer_server(),
+            crate::os::xous::services::TicktimerScalar::UnlockMutex(self.index()).into(),
+        )
+        .expect("failure to send UnlockMutex command");
+    }
+
+    #[inline]
+    pub unsafe fn try_lock(&self) -> bool {
+        self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok()
+    }
+
+    #[inline]
+    pub unsafe fn try_lock_or_poison(&self) -> bool {
+        self.locked.fetch_add(1, SeqCst) == 0
+    }
+}
+
+impl Drop for Mutex {
+    fn drop(&mut self) {
+        // If there was Mutex contention, then we involved the ticktimer. Free
+        // the resources associated with this Mutex as it is deallocated.
+        if self.contended.load(Relaxed) {
+            scalar(
+                ticktimer_server(),
+                crate::os::xous::services::TicktimerScalar::FreeMutex(self.index()).into(),
+            )
+            .ok();
+        }
+    }
+}
diff --git a/library/std/src/sys/xous/locks/rwlock.rs b/library/std/src/sys/xous/locks/rwlock.rs
new file mode 100644
index 00000000000..618da758adf
--- /dev/null
+++ b/library/std/src/sys/xous/locks/rwlock.rs
@@ -0,0 +1,72 @@
+use crate::os::xous::ffi::do_yield;
+use crate::sync::atomic::{AtomicIsize, Ordering::SeqCst};
+
+pub struct RwLock {
+    /// The "mode" value indicates how many threads are waiting on this
+    /// Mutex. Possible values are:
+    ///    -1: The lock is locked for writing
+    ///     0: The lock is unlocked
+    ///   >=1: The lock is locked for reading
+    ///
+    /// This currently spins waiting for the lock to be freed. An
+    /// optimization would be to involve the ticktimer server to
+    /// coordinate unlocks.
+    mode: AtomicIsize,
+}
+
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {}
+
+impl RwLock {
+    #[inline]
+    #[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
+    pub const fn new() -> RwLock {
+        RwLock { mode: AtomicIsize::new(0) }
+    }
+
+    #[inline]
+    pub unsafe fn read(&self) {
+        while !unsafe { self.try_read() } {
+            do_yield();
+        }
+    }
+
+    #[inline]
+    pub unsafe fn try_read(&self) -> bool {
+        // Non-atomically determine the current value.
+        let current = self.mode.load(SeqCst);
+
+        // If it's currently locked for writing, then we cannot read.
+        if current < 0 {
+            return false;
+        }
+
+        // Attempt to lock. If the `current` value has changed, then this
+        // operation will fail and we will not obtain the lock even if we
+        // could potentially keep it.
+        let new = current + 1;
+        self.mode.compare_exchange(current, new, SeqCst, SeqCst).is_ok()
+    }
+
+    #[inline]
+    pub unsafe fn write(&self) {
+        while !unsafe { self.try_write() } {
+            do_yield();
+        }
+    }
+
+    #[inline]
+    pub unsafe fn try_write(&self) -> bool {
+        self.mode.compare_exchange(0, -1, SeqCst, SeqCst).is_ok()
+    }
+
+    #[inline]
+    pub unsafe fn read_unlock(&self) {
+        self.mode.fetch_sub(1, SeqCst);
+    }
+
+    #[inline]
+    pub unsafe fn write_unlock(&self) {
+        assert_eq!(self.mode.compare_exchange(-1, 0, SeqCst, SeqCst), Ok(-1));
+    }
+}
diff --git a/library/std/src/sys/xous/mod.rs b/library/std/src/sys/xous/mod.rs
new file mode 100644
index 00000000000..6d5c218d195
--- /dev/null
+++ b/library/std/src/sys/xous/mod.rs
@@ -0,0 +1,37 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+pub mod alloc;
+#[path = "../unsupported/args.rs"]
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+#[path = "../unsupported/env.rs"]
+pub mod env;
+#[path = "../unsupported/fs.rs"]
+pub mod fs;
+#[path = "../unsupported/io.rs"]
+pub mod io;
+pub mod locks;
+#[path = "../unsupported/net.rs"]
+pub mod net;
+#[path = "../unsupported/once.rs"]
+pub mod once;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub mod thread;
+pub mod thread_local_key;
+#[path = "../unsupported/thread_parking.rs"]
+pub mod thread_parking;
+pub mod time;
+
+#[path = "../unsupported/common.rs"]
+mod common;
+pub use common::*;
diff --git a/library/std/src/sys/xous/os.rs b/library/std/src/sys/xous/os.rs
new file mode 100644
index 00000000000..3d19fa4b31a
--- /dev/null
+++ b/library/std/src/sys/xous/os.rs
@@ -0,0 +1,147 @@
+use super::unsupported;
+use crate::error::Error as StdError;
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::os::xous::ffi::Error as XousError;
+use crate::path::{self, PathBuf};
+
+#[cfg(not(test))]
+mod c_compat {
+    use crate::os::xous::ffi::exit;
+    extern "C" {
+        fn main() -> u32;
+    }
+
+    #[no_mangle]
+    pub extern "C" fn abort() {
+        exit(1);
+    }
+
+    #[no_mangle]
+    pub extern "C" fn _start() {
+        exit(unsafe { main() });
+    }
+
+    // This function is needed by the panic runtime. The symbol is named in
+    // pre-link args for the target specification, so keep that in sync.
+    #[no_mangle]
+    // NB. used by both libunwind and libpanic_abort
+    pub extern "C" fn __rust_abort() -> ! {
+        exit(101);
+    }
+}
+
+pub fn errno() -> i32 {
+    0
+}
+
+pub fn error_string(errno: i32) -> String {
+    Into::<XousError>::into(errno).to_string()
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+    panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+    type Item = PathBuf;
+    fn next(&mut self) -> Option<PathBuf> {
+        self.0
+    }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+    I: Iterator<Item = T>,
+    T: AsRef<OsStr>,
+{
+    Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "not supported on this platform yet".fmt(f)
+    }
+}
+
+impl StdError for JoinPathsError {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "not supported on this platform yet"
+    }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub struct Env(!);
+
+impl Env {
+    // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
+    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
+        let Self(inner) = self;
+        match *inner {}
+    }
+}
+
+impl fmt::Debug for Env {
+    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let Self(inner) = self;
+        match *inner {}
+    }
+}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        self.0
+    }
+}
+
+pub fn env() -> Env {
+    panic!("not supported on this platform")
+}
+
+pub fn getenv(_: &OsStr) -> Option<OsString> {
+    None
+}
+
+pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
+    Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
+}
+
+pub fn unsetenv(_: &OsStr) -> io::Result<()> {
+    Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
+}
+
+pub fn temp_dir() -> PathBuf {
+    panic!("no filesystem on this platform")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+    None
+}
+
+pub fn exit(code: i32) -> ! {
+    crate::os::xous::ffi::exit(code as u32);
+}
+
+pub fn getpid() -> u32 {
+    panic!("no pids on this platform")
+}
diff --git a/library/std/src/sys/xous/stdio.rs b/library/std/src/sys/xous/stdio.rs
new file mode 100644
index 00000000000..2ac694641ba
--- /dev/null
+++ b/library/std/src/sys/xous/stdio.rs
@@ -0,0 +1,131 @@
+use crate::io;
+
+pub struct Stdin;
+pub struct Stdout {}
+pub struct Stderr;
+
+use crate::os::xous::ffi::{lend, try_lend, try_scalar, Connection};
+use crate::os::xous::services::{log_server, try_connect, LogScalar};
+
+impl Stdin {
+    pub const fn new() -> Stdin {
+        Stdin
+    }
+}
+
+impl io::Read for Stdin {
+    fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
+        Ok(0)
+    }
+}
+
+impl Stdout {
+    pub const fn new() -> Stdout {
+        Stdout {}
+    }
+}
+
+impl io::Write for Stdout {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        #[repr(align(4096))]
+        struct LendBuffer([u8; 4096]);
+        let mut lend_buffer = LendBuffer([0u8; 4096]);
+        let connection = log_server();
+        for chunk in buf.chunks(lend_buffer.0.len()) {
+            for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) {
+                *dest = *src;
+            }
+            lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap();
+        }
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl Stderr {
+    pub const fn new() -> Stderr {
+        Stderr
+    }
+}
+
+impl io::Write for Stderr {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        #[repr(align(4096))]
+        struct LendBuffer([u8; 4096]);
+        let mut lend_buffer = LendBuffer([0u8; 4096]);
+        let connection = log_server();
+        for chunk in buf.chunks(lend_buffer.0.len()) {
+            for (dest, src) in lend_buffer.0.iter_mut().zip(chunk) {
+                *dest = *src;
+            }
+            lend(connection, 1, &lend_buffer.0, 0, chunk.len()).unwrap();
+        }
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+    true
+}
+
+#[derive(Copy, Clone)]
+pub struct PanicWriter {
+    log: Connection,
+    gfx: Option<Connection>,
+}
+
+impl io::Write for PanicWriter {
+    fn write(&mut self, s: &[u8]) -> core::result::Result<usize, io::Error> {
+        for c in s.chunks(core::mem::size_of::<usize>() * 4) {
+            // Text is grouped into 4x `usize` words. The id is 1100 plus
+            // the number of characters in this message.
+            // Ignore errors since we're already panicking.
+            try_scalar(self.log, LogScalar::AppendPanicMessage(&c).into()).ok();
+        }
+
+        // Serialize the text to the graphics panic handler, only if we were able
+        // to acquire a connection to it. Text length is encoded in the `valid` field,
+        // the data itself in the buffer. Typically several messages are require to
+        // fully transmit the entire panic message.
+        if let Some(gfx) = self.gfx {
+            #[repr(C, align(4096))]
+            struct Request([u8; 4096]);
+            let mut request = Request([0u8; 4096]);
+            for (&s, d) in s.iter().zip(request.0.iter_mut()) {
+                *d = s;
+            }
+            try_lend(gfx, 0 /* AppendPanicText */, &request.0, 0, s.len()).ok();
+        }
+        Ok(s.len())
+    }
+
+    // Tests show that this does not seem to be reliably called at the end of a panic
+    // print, so, we can't rely on this to e.g. trigger a graphics update.
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+    // Generally this won't fail because every server has already connected, so
+    // this is likely to succeed.
+    let log = log_server();
+
+    // Send the "We're panicking" message (1000).
+    try_scalar(log, LogScalar::BeginPanic.into()).ok();
+
+    // This is will fail in the case that the connection table is full, or if the
+    // graphics server is not running. Most servers do not already have this connection.
+    let gfx = try_connect("panic-to-screen!");
+
+    Some(PanicWriter { log, gfx })
+}
diff --git a/library/std/src/sys/xous/thread.rs b/library/std/src/sys/xous/thread.rs
new file mode 100644
index 00000000000..78c68de7bf3
--- /dev/null
+++ b/library/std/src/sys/xous/thread.rs
@@ -0,0 +1,144 @@
+use crate::ffi::CStr;
+use crate::io;
+use crate::num::NonZeroUsize;
+use crate::os::xous::ffi::{
+    blocking_scalar, create_thread, do_yield, join_thread, map_memory, update_memory_flags,
+    MemoryFlags, Syscall, ThreadId,
+};
+use crate::os::xous::services::{ticktimer_server, TicktimerScalar};
+use crate::time::Duration;
+use core::arch::asm;
+
+pub struct Thread {
+    tid: ThreadId,
+}
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 131072;
+const MIN_STACK_SIZE: usize = 4096;
+pub const GUARD_PAGE_SIZE: usize = 4096;
+
+impl Thread {
+    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+        let p = Box::into_raw(Box::new(p));
+        let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE);
+
+        if (stack_size & 4095) != 0 {
+            stack_size = (stack_size + 4095) & !4095;
+        }
+
+        // Allocate the whole thing, then divide it up after the fact. This ensures that
+        // even if there's a context switch during this function, the whole stack plus
+        // guard pages will remain contiguous.
+        let stack_plus_guard_pages: &mut [u8] = unsafe {
+            map_memory(
+                None,
+                None,
+                GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE,
+                MemoryFlags::R | MemoryFlags::W | MemoryFlags::X,
+            )
+        }
+        .map_err(|code| io::Error::from_raw_os_error(code as i32))?;
+
+        // No access to this page. Note: Write-only pages are illegal, and will
+        // cause an access violation.
+        unsafe {
+            update_memory_flags(&mut stack_plus_guard_pages[0..GUARD_PAGE_SIZE], MemoryFlags::W)
+                .map_err(|code| io::Error::from_raw_os_error(code as i32))?
+        };
+
+        // No access to this page. Note: Write-only pages are illegal, and will
+        // cause an access violation.
+        unsafe {
+            update_memory_flags(
+                &mut stack_plus_guard_pages[(GUARD_PAGE_SIZE + stack_size)..],
+                MemoryFlags::W,
+            )
+            .map_err(|code| io::Error::from_raw_os_error(code as i32))?
+        };
+
+        let guard_page_pre = stack_plus_guard_pages.as_ptr() as usize;
+        let tid = create_thread(
+            thread_start as *mut usize,
+            &mut stack_plus_guard_pages[GUARD_PAGE_SIZE..(stack_size + GUARD_PAGE_SIZE)],
+            p as usize,
+            guard_page_pre,
+            stack_size,
+            0,
+        )
+        .map_err(|code| io::Error::from_raw_os_error(code as i32))?;
+
+        extern "C" fn thread_start(main: *mut usize, guard_page_pre: usize, stack_size: usize) {
+            unsafe {
+                // Finally, let's run some code.
+                Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+            }
+
+            // Destroy TLS, which will free the TLS page and call the destructor for
+            // any thread local storage.
+            unsafe {
+                crate::sys::thread_local_key::destroy_tls();
+            }
+
+            // Deallocate the stack memory, along with the guard pages. Afterwards,
+            // exit the thread by returning to the magic address 0xff80_3000usize,
+            // which tells the kernel to deallocate this thread.
+            let mapped_memory_base = guard_page_pre;
+            let mapped_memory_length = GUARD_PAGE_SIZE + stack_size + GUARD_PAGE_SIZE;
+            unsafe {
+                asm!(
+                    "ecall",
+                    "ret",
+                                        in("a0") Syscall::UnmapMemory as usize,
+                                        in("a1") mapped_memory_base,
+                                        in("a2") mapped_memory_length,
+                                        in("ra") 0xff80_3000usize,
+                                        options(nomem, nostack, noreturn)
+                );
+            }
+        }
+
+        Ok(Thread { tid })
+    }
+
+    pub fn yield_now() {
+        do_yield();
+    }
+
+    pub fn set_name(_name: &CStr) {
+        // nope
+    }
+
+    pub fn sleep(dur: Duration) {
+        // Because the sleep server works on units of `usized milliseconds`, split
+        // the messages up into these chunks. This means we may run into issues
+        // if you try to sleep a thread for more than 49 days on a 32-bit system.
+        let mut millis = dur.as_millis();
+        while millis > 0 {
+            let sleep_duration =
+                if millis > (usize::MAX as _) { usize::MAX } else { millis as usize };
+            blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into())
+                .expect("failed to send message to ticktimer server");
+            millis -= sleep_duration as u128;
+        }
+    }
+
+    pub fn join(self) {
+        join_thread(self.tid).unwrap();
+    }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+    // We're unicore right now.
+    Ok(unsafe { NonZeroUsize::new_unchecked(1) })
+}
+
+pub mod guard {
+    pub type Guard = !;
+    pub unsafe fn current() -> Option<Guard> {
+        None
+    }
+    pub unsafe fn init() -> Option<Guard> {
+        None
+    }
+}
diff --git a/library/std/src/sys/xous/thread_local_key.rs b/library/std/src/sys/xous/thread_local_key.rs
new file mode 100644
index 00000000000..3771ea65700
--- /dev/null
+++ b/library/std/src/sys/xous/thread_local_key.rs
@@ -0,0 +1,190 @@
+use crate::mem::ManuallyDrop;
+use crate::ptr;
+use crate::sync::atomic::AtomicPtr;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use core::arch::asm;
+
+use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags};
+
+/// Thread Local Storage
+///
+/// Currently, we are limited to 1023 TLS entries. The entries
+/// live in a page of memory that's unique per-process, and is
+/// stored in the `$tp` register. If this register is 0, then
+/// TLS has not been initialized and thread cleanup can be skipped.
+///
+/// The index into this register is the `key`. This key is identical
+/// between all threads, but indexes a different offset within this
+/// pointer.
+pub type Key = usize;
+
+pub type Dtor = unsafe extern "C" fn(*mut u8);
+
+const TLS_MEMORY_SIZE: usize = 4096;
+
+/// TLS keys start at `1` to mimic POSIX.
+static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1);
+
+fn tls_ptr_addr() -> *mut usize {
+    let mut tp: usize;
+    unsafe {
+        asm!(
+            "mv {}, tp",
+            out(reg) tp,
+        );
+    }
+    core::ptr::from_exposed_addr_mut::<usize>(tp)
+}
+
+/// Create an area of memory that's unique per thread. This area will
+/// contain all thread local pointers.
+fn tls_ptr() -> *mut usize {
+    let mut tp = tls_ptr_addr();
+
+    // If the TP register is `0`, then this thread hasn't initialized
+    // its TLS yet. Allocate a new page to store this memory.
+    if tp.is_null() {
+        tp = unsafe {
+            map_memory(
+                None,
+                None,
+                TLS_MEMORY_SIZE / core::mem::size_of::<usize>(),
+                MemoryFlags::R | MemoryFlags::W,
+            )
+        }
+        .expect("Unable to allocate memory for thread local storage")
+        .as_mut_ptr();
+
+        unsafe {
+            // Key #0 is currently unused.
+            (tp).write_volatile(0);
+
+            // Set the thread's `$tp` register
+            asm!(
+                "mv tp, {}",
+                in(reg) tp as usize,
+            );
+        }
+    }
+    tp
+}
+
+/// Allocate a new TLS key. These keys are shared among all threads.
+fn tls_alloc() -> usize {
+    TLS_KEY_INDEX.fetch_add(1, SeqCst)
+}
+
+#[inline]
+pub unsafe fn create(dtor: Option<Dtor>) -> Key {
+    let key = tls_alloc();
+    if let Some(f) = dtor {
+        unsafe { register_dtor(key, f) };
+    }
+    key
+}
+
+#[inline]
+pub unsafe fn set(key: Key, value: *mut u8) {
+    assert!((key < 1022) && (key >= 1));
+    unsafe { tls_ptr().add(key).write_volatile(value as usize) };
+}
+
+#[inline]
+pub unsafe fn get(key: Key) -> *mut u8 {
+    assert!((key < 1022) && (key >= 1));
+    core::ptr::from_exposed_addr_mut::<u8>(unsafe { tls_ptr().add(key).read_volatile() })
+}
+
+#[inline]
+pub unsafe fn destroy(_key: Key) {
+    panic!("can't destroy keys on Xous");
+}
+
+// -------------------------------------------------------------------------
+// Dtor registration (stolen from Windows)
+//
+// Xous has no native support for running destructors so we manage our own
+// list of destructors to keep track of how to destroy keys. We then install a
+// callback later to get invoked whenever a thread exits, running all
+// appropriate destructors.
+//
+// Currently unregistration from this list is not supported. A destructor can be
+// registered but cannot be unregistered. There's various simplifying reasons
+// for doing this, the big ones being:
+//
+// 1. Currently we don't even support deallocating TLS keys, so normal operation
+//    doesn't need to deallocate a destructor.
+// 2. There is no point in time where we know we can unregister a destructor
+//    because it could always be getting run by some remote thread.
+//
+// Typically processes have a statically known set of TLS keys which is pretty
+// small, and we'd want to keep this memory alive for the whole process anyway
+// really.
+//
+// Perhaps one day we can fold the `Box` here into a static allocation,
+// expanding the `StaticKey` structure to contain not only a slot for the TLS
+// key but also a slot for the destructor queue on windows. An optimization for
+// another day!
+
+static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
+
+struct Node {
+    dtor: Dtor,
+    key: Key,
+    next: *mut Node,
+}
+
+unsafe fn register_dtor(key: Key, dtor: Dtor) {
+    let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() }));
+
+    let mut head = DTORS.load(SeqCst);
+    loop {
+        node.next = head;
+        match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) {
+            Ok(_) => return, // nothing to drop, we successfully added the node to the list
+            Err(cur) => head = cur,
+        }
+    }
+}
+
+pub unsafe fn destroy_tls() {
+    let tp = tls_ptr_addr();
+
+    // If the pointer address is 0, then this thread has no TLS.
+    if tp.is_null() {
+        return;
+    }
+    unsafe { run_dtors() };
+
+    // Finally, free the TLS array
+    unsafe {
+        unmap_memory(core::slice::from_raw_parts_mut(
+            tp,
+            TLS_MEMORY_SIZE / core::mem::size_of::<usize>(),
+        ))
+        .unwrap()
+    };
+}
+
+unsafe fn run_dtors() {
+    let mut any_run = true;
+    for _ in 0..5 {
+        if !any_run {
+            break;
+        }
+        any_run = false;
+        let mut cur = DTORS.load(SeqCst);
+        while !cur.is_null() {
+            let ptr = unsafe { get((*cur).key) };
+
+            if !ptr.is_null() {
+                unsafe { set((*cur).key, ptr::null_mut()) };
+                unsafe { ((*cur).dtor)(ptr as *mut _) };
+                any_run = true;
+            }
+
+            unsafe { cur = (*cur).next };
+        }
+    }
+}
diff --git a/library/std/src/sys/xous/time.rs b/library/std/src/sys/xous/time.rs
new file mode 100644
index 00000000000..4e4ae67efff
--- /dev/null
+++ b/library/std/src/sys/xous/time.rs
@@ -0,0 +1,57 @@
+use crate::os::xous::ffi::blocking_scalar;
+use crate::os::xous::services::{
+    systime_server, ticktimer_server, SystimeScalar::GetUtcTimeMs, TicktimerScalar::ElapsedMs,
+};
+use crate::time::Duration;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(Duration);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(Duration);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+
+impl Instant {
+    pub fn now() -> Instant {
+        let result = blocking_scalar(ticktimer_server(), ElapsedMs.into())
+            .expect("failed to request elapsed_ms");
+        let lower = result[0];
+        let upper = result[1];
+        Instant { 0: Duration::from_millis(lower as u64 | (upper as u64) << 32) }
+    }
+
+    pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+        self.0.checked_sub(other.0)
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+        self.0.checked_add(*other).map(Instant)
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+        self.0.checked_sub(*other).map(Instant)
+    }
+}
+
+impl SystemTime {
+    pub fn now() -> SystemTime {
+        let result = blocking_scalar(systime_server(), GetUtcTimeMs.into())
+            .expect("failed to request utc time in ms");
+        let lower = result[0];
+        let upper = result[1];
+        SystemTime { 0: Duration::from_millis((upper as u64) << 32 | lower as u64) }
+    }
+
+    pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+        self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_add(*other)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_sub(*other)?))
+    }
+}
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index e9c727cbbd1..f7d82175063 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -46,6 +46,7 @@ cfg_if::cfg_if! {
     if #[cfg(any(target_os = "l4re",
                  feature = "restricted-std",
                  all(target_family = "wasm", not(target_os = "emscripten")),
+                 target_os = "xous",
                  all(target_vendor = "fortanix", target_env = "sgx")))] {
         pub use crate::sys::net;
     } else {
diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs
index 88d937a7db1..8d51732e035 100644
--- a/library/std/src/sys_common/thread_info.rs
+++ b/library/std/src/sys_common/thread_info.rs
@@ -1,46 +1,51 @@
 #![allow(dead_code)] // stack_guard isn't used right now on all platforms
 
-use crate::cell::RefCell;
+use crate::cell::OnceCell;
 use crate::sys::thread::guard::Guard;
 use crate::thread::Thread;
 
 struct ThreadInfo {
-    stack_guard: Option<Guard>,
-    thread: Thread,
+    stack_guard: OnceCell<Guard>,
+    thread: OnceCell<Thread>,
 }
 
-thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = const { RefCell::new(None) } }
+thread_local! {
+   static THREAD_INFO: ThreadInfo = const { ThreadInfo {
+       stack_guard: OnceCell::new(),
+       thread: OnceCell::new()
+   } };
+}
 
 impl ThreadInfo {
     fn with<R, F>(f: F) -> Option<R>
     where
-        F: FnOnce(&mut ThreadInfo) -> R,
+        F: FnOnce(&Thread, &OnceCell<Guard>) -> R,
     {
         THREAD_INFO
             .try_with(move |thread_info| {
-                let mut thread_info = thread_info.borrow_mut();
-                let thread_info = thread_info.get_or_insert_with(|| ThreadInfo {
-                    stack_guard: None,
-                    thread: Thread::new(None),
-                });
-                f(thread_info)
+                let thread = thread_info.thread.get_or_init(|| Thread::new(None));
+                f(thread, &thread_info.stack_guard)
             })
             .ok()
     }
 }
 
 pub fn current_thread() -> Option<Thread> {
-    ThreadInfo::with(|info| info.thread.clone())
+    ThreadInfo::with(|thread, _| thread.clone())
 }
 
 pub fn stack_guard() -> Option<Guard> {
-    ThreadInfo::with(|info| info.stack_guard.clone()).and_then(|o| o)
+    ThreadInfo::with(|_, guard| guard.get().cloned()).flatten()
 }
 
+/// Set new thread info, panicking if it has already been initialized
+#[allow(unreachable_code, unreachable_patterns)] // some platforms don't use stack_guard
 pub fn set(stack_guard: Option<Guard>, thread: Thread) {
     THREAD_INFO.with(move |thread_info| {
-        let mut thread_info = thread_info.borrow_mut();
-        rtassert!(thread_info.is_none());
-        *thread_info = Some(ThreadInfo { stack_guard, thread });
+        rtassert!(thread_info.stack_guard.get().is_none() && thread_info.thread.get().is_none());
+        if let Some(guard) = stack_guard {
+            thread_info.stack_guard.set(guard).unwrap();
+        }
+        thread_info.thread.set(thread).unwrap();
     });
 }
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 50f1007e1ff..46a62eed952 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -1640,7 +1640,10 @@ impl<'a> Builder<'a> {
                 // flesh out rpath support more fully in the future.
                 rustflags.arg("-Zosx-rpath-install-name");
                 Some(format!("-Wl,-rpath,@loader_path/../{libdir}"))
-            } else if !target.contains("windows") && !target.contains("aix") {
+            } else if !target.contains("windows")
+                && !target.contains("aix")
+                && !target.contains("xous")
+            {
                 rustflags.arg("-Clink-args=-Wl,-z,origin");
                 Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}"))
             } else {
diff --git a/src/bootstrap/job.rs b/src/bootstrap/job.rs
index 4fb00f65dc1..b0a97b540ec 100644
--- a/src/bootstrap/job.rs
+++ b/src/bootstrap/job.rs
@@ -134,7 +134,7 @@ pub unsafe fn setup(build: &mut Build) {
     // If this failed, well at least we tried! An example of DuplicateHandle
     // failing in the past has been when the wrong python2 package spawned this
     // build system (e.g., the `python2` package in MSYS instead of
-    // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
+    // `mingw-w64-x86_64-python2`). Not sure why it failed, but the "failure
     // mode" here is that we only clean everything up when the build system
     // dies, not when the python parent does, so not too bad.
     if r.is_err() {
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh
index 390304b6ad5..918b19612ae 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-15/script.sh
@@ -4,15 +4,18 @@ set -ex
 
 # Only run the stage 1 tests on merges, not on PR CI jobs.
 if [[ -z "${PR_CI_JOB}" ]]; then
-../x.py --stage 1 test --skip src/tools/tidy && \
-           # Run the `mir-opt` tests again but this time for a 32-bit target.
-           # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
-           # both 32-bit and 64-bit outputs updated by the PR author, before
-           # the PR is approved and tested for merging.
-           # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`,
-           # despite having different output on 32-bit vs 64-bit targets.
-           ../x.py --stage 1 test tests/mir-opt \
-                             --host='' --target=i686-unknown-linux-gnu
+     ../x.py --stage 1 test --skip src/tools/tidy && \
+     # Run the `mir-opt` tests again but this time for a 32-bit target.
+     # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
+     # both 32-bit and 64-bit outputs updated by the PR author, before
+     # the PR is approved and tested for merging.
+     # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`,
+     # despite having different output on 32-bit vs 64-bit targets.
+     ../x.py --stage 1 test tests/mir-opt \
+          --host='' --target=i686-unknown-linux-gnu && \
+     # Run `ui-fulldeps` in `--stage=1`, which actually uses the stage0
+     # compiler, and is sensitive to the addition of new flags.
+     ../x.py --stage 1 test tests/ui-fulldeps
 fi
 
 # NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
index 0d67319168d..50b6386a009 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
@@ -1 +1 @@
-0.16.9
\ No newline at end of file
+0.16.10
\ No newline at end of file
diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md
index 12a8b2b8db4..3b6883c0d55 100644
--- a/src/doc/rustdoc/src/SUMMARY.md
+++ b/src/doc/rustdoc/src/SUMMARY.md
@@ -4,6 +4,7 @@
 - [Command-line arguments](command-line-arguments.md)
 - [How to read rustdoc output](how-to-read-rustdoc.md)
     - [In-doc settings](read-documentation/in-doc-settings.md)
+    - [Search](read-documentation/search.md)
 - [How to write documentation](how-to-write-documentation.md)
     - [What to include (and exclude)](write-documentation/what-to-include.md)
     - [The `#[doc]` attribute](write-documentation/the-doc-attribute.md)
diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md
index cd6e29ffd66..dade62c5423 100644
--- a/src/doc/rustdoc/src/how-to-read-rustdoc.md
+++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md
@@ -75,56 +75,11 @@ or the current item whose documentation is being displayed.
 ## The Theme Picker and Search Interface
 
 When viewing `rustdoc`'s output in a browser with JavaScript enabled,
-a dynamic interface appears at the top of the page composed of the search
-interface, help screen, and options.
-
-### The Search Interface
-
-Typing in the search bar instantly searches the available documentation for
-the string entered with a fuzzy matching algorithm that is tolerant of minor
-typos.
-
-By default, the search results given are "In Names",
-meaning that the fuzzy match is made against the names of items.
-Matching names are shown on the left, and the first few words of their
-descriptions are given on the right.
-By clicking an item, you will navigate to its particular documentation.
-
-There are two other sets of results, shown as tabs in the search results pane.
-"In Parameters" shows matches for the string in the types of parameters to
-functions, and "In Return Types" shows matches in the return types of functions.
-Both are very useful when looking for a function whose name you can't quite
-bring to mind when you know the type you have or want.
-
-Names in the search interface can be prefixed with an item type followed by a
-colon (such as `mod:`) to restrict the results to just that kind of item. Also,
-searching for `println!` will search for a macro named `println`, just like
-searching for `macro:println` does.
-
-Function signature searches can query generics, wrapped in angle brackets, and
-traits are normalized like types in the search engine. For example, a function
-with the signature `fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
-can be matched with the following queries:
-
-* `Iterator<u32> -> usize`
-* `trait:Iterator<primitive:u32> -> primitive:usize`
-* `Iterator -> usize`
-
-Generics and function parameters are order-agnostic, but sensitive to nesting
-and number of matches. For example, a function with the signature
-`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
-will match these queries:
-
-* `Read -> Result<Vec<u8>, Error>`
-* `Read -> Result<Error, Vec>`
-* `Read -> Result<Vec<u8>>`
-
-But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
-
-Function signature searches also support arrays and slices. The explicit name
-`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
-or array of bytes, while square brackets `[u8]` will match either one. Empty
-square brackets, `[]`, will match any slice regardless of what it contains.
+a dynamic interface appears at the top of the page composed of the [search]
+interface, help screen, and [options].
+
+[options]: read-documentation/in-doc-settings.html
+[search]: read-documentation/search.md
 
 Paths are supported as well, you can look for `Vec::new` or `Option::Some` or
 even `module::module_child::another_child::struct::field`. Whitespace characters
diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md
new file mode 100644
index 00000000000..56a5016d0ce
--- /dev/null
+++ b/src/doc/rustdoc/src/read-documentation/search.md
@@ -0,0 +1,237 @@
+# Rustdoc search
+
+Typing in the search bar instantly searches the available documentation,
+matching either the name and path of an item, or a function's approximate
+type signature.
+
+## Search By Name
+
+To search by the name of an item (items include modules, types, traits,
+functions, and macros), write its name or path. As a special case, the parts
+of a path that normally get divided by `::` double colons can instead be
+separated by spaces. For example:
+
+  * [`vec new`] and [`vec::new`] both show the function `std::vec::Vec::new`
+    as a result.
+  * [`vec`], [`vec vec`], [`std::vec`], and [`std::vec::Vec`] all include the struct
+    `std::vec::Vec` itself in the results (and all but the last one also
+    include the module in the results).
+
+[`vec new`]: ../../std/vec/struct.Vec.html?search=vec%20new&filter-crate=std
+[`vec::new`]: ../../std/vec/struct.Vec.html?search=vec::new&filter-crate=std
+[`vec`]: ../../std/vec/struct.Vec.html?search=vec&filter-crate=std
+[`vec vec`]: ../../std/vec/struct.Vec.html?search=vec%20vec&filter-crate=std
+[`std::vec`]: ../../std/vec/struct.Vec.html?search=std::vec&filter-crate=std
+[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
+[`std::vec::Vec`]: ../../std/vec/struct.Vec.html?search=std::vec::Vec&filter-crate=std
+
+As a quick way to trim down the list of results, there's a drop-down selector
+below the search input, labeled "Results in \[std\]". Clicking it can change
+which crate is being searched.
+
+Rustdoc uses a fuzzy matching function that can tolerate typos for this,
+though it's based on the length of the name that's typed in, so a good example
+of how this works would be [`HahsMap`]. To avoid this, wrap the item in quotes,
+searching for `"HahsMap"` (in this example, no results will be returned).
+
+[`HahsMap`]: ../../std/collections/struct.HashMap.html?search=HahsMap&filter-crate=std
+
+### Tabs in the Search By Name interface
+
+In fact, using [`HahsMap`] again as the example, it tells you that you're
+using "In Names" by default, but also lists two other tabs below the crate
+drop-down: "In Parameters" and "In Return Types".
+
+These two tabs are lists of functions, defined on the closest matching type
+to the search (for `HahsMap`, it loudly auto-corrects to `hashmap`). This
+auto-correct only kicks in if nothing is found that matches the literal.
+
+These tabs are not just methods. For example, searching the alloc crate for
+[`Layout`] also lists functions that accept layouts even though they're
+methods on the allocator or free functions.
+
+[`Layout`]: ../../alloc/index.html?search=Layout&filter-crate=alloc
+
+## Searching By Type Signature for functions
+
+If you know more specifically what the function you want to look at does,
+Rustdoc can search by more than one type at once in the parameters and return
+value. Multiple parameters are separated by `,` commas, and the return value
+is written with after a `->` arrow.
+
+Before describing the syntax in more detail, here's a few sample searches of
+the standard library and functions that are included in the results list:
+
+| Query | Results |
+|-------|--------|
+| [`usize -> vec`][] | `slice::repeat` and `Vec::with_capacity` |
+| [`vec, vec -> bool`][] | `Vec::eq` |
+| [`option<T>, fnonce -> option<U>`][] | `Option::map` and `Option::and_then` |
+| [`option<T>, fnonce -> option<T>`][] | `Option::filter` and `Option::inspect` |
+| [`option -> default`][] | `Option::unwrap_or_default` |
+| [`stdout, [u8]`][stdoutu8] | `Stdout::write` |
+| [`any -> !`][] | `panic::panic_any` |
+| [`vec::intoiter<T> -> [T]`][iterasslice] | `IntoIter::as_slice` and `IntoIter::next_chunk` |
+
+[`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std
+[`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std
+[`option<T>, fnonce -> option<U>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<U>&filter-crate=std
+[`option<T>, fnonce -> option<T>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<T>&filter-crate=std
+[`option -> default`]: ../../std/vec/struct.Vec.html?search=option%20-%3E%20default&filter-crate=std
+[`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std
+[stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std
+[iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter<T>%20->%20[T]&filter-crate=std
+
+### How type-based search works
+
+In a complex type-based search, Rustdoc always treats every item's name as literal.
+If a name is used and nothing in the docs matches the individual item, such as
+a typo-ed [`uize -> vec`][] search, the item `uize` is treated as a generic
+type parameter (resulting in `vec::from` and other generic vec constructors).
+
+[`uize -> vec`]: ../../std/vec/struct.Vec.html?search=uize%20-%3E%20vec&filter-crate=std
+
+After deciding which items are type parameters and which are actual types, it
+then searches by matching up the function parameters (written before the `->`)
+and the return types (written after the `->`). Type matching is order-agnostic,
+and allows items to be left out of the query, but items that are present in the
+query must be present in the function for it to match.
+
+Function signature searches can query generics, wrapped in angle brackets, and
+traits will be normalized like types in the search engine if no type parameters
+match them. For example, a function with the signature
+`fn my_function<I: Iterator<Item=u32>>(input: I) -> usize`
+can be matched with the following queries:
+
+* `Iterator<u32> -> usize`
+* `Iterator -> usize`
+
+Generics and function parameters are order-agnostic, but sensitive to nesting
+and number of matches. For example, a function with the signature
+`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
+will match these queries:
+
+* `Read -> Result<Vec<u8>, Error>`
+* `Read -> Result<Error, Vec>`
+* `Read -> Result<Vec<u8>>`
+
+But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
+
+Function signature searches also support arrays and slices. The explicit name
+`primitive:slice<u8>` and `primitive:array<u8>` can be used to match a slice
+or array of bytes, while square brackets `[u8]` will match either one. Empty
+square brackets, `[]`, will match any slice or array regardless of what
+it contains, while a slice with a type parameter, like `[T]`, will only match
+functions that actually operate on generic slices.
+
+### Limitations and quirks of type-based search
+
+Type-based search is still a buggy, experimental, work-in-progress feature.
+Most of these limitations should be addressed in future version of Rustdoc.
+
+  * There's no way to write trait constraints on generic parameters.
+    You can name traits directly, and if there's a type parameter
+    with that bound, it'll match, but `option<T> -> T where T: Default`
+    cannot be precisely searched for (use `option<Default> -> Default`).
+
+  * Type parameters match type parameters, such that `Option<A>` matches
+    `Option<T>`, but never match concrete types in function signatures.
+    A trait named as if it were a type, such as `Option<Read>`, will match
+    a type parameter constrained by that trait, such as
+    `Option<T> where T: Read`, as well as matching `dyn Trait` and
+    `impl Trait`.
+
+  * `impl Trait` in argument position is treated exactly like a type
+    parameter, but in return position it will not match type parameters.
+
+  * Any type named in a complex type-based search will be assumed to be a
+    type parameter if nothing matching the name exactly is found. If you
+    want to force a type parameter, write `generic:T` and it will be used
+    as a type parameter even if a matching name is found. If you know
+    that you don't want a type parameter, you can force it to match
+    something else by giving it a different prefix like `struct:T`.
+
+  * It's impossible to search for references, pointers, or tuples. The
+    wrapped types can be searched for, so a function that takes `&File` can
+    be found with `File`, but you'll get a parse error when typing an `&`
+    into the search field. Similarly, `Option<(T, U)>` can be matched with
+    `Option<T, U>`, but `(` will give a parse error.
+
+  * Searching for lifetimes is not supported.
+
+  * It's impossible to search for closures based on their parameters or
+    return values.
+
+  * It's impossible to search based on the length of an array.
+
+## Item filtering
+
+Names in the search interface can be prefixed with an item type followed by a
+colon (such as `mod:`) to restrict the results to just that kind of item. Also,
+searching for `println!` will search for a macro named `println`, just like
+searching for `macro:println` does. The complete list of available filters is
+given under the <kbd>?</kbd> Help area, and in the detailed syntax below.
+
+Item filters can be used in both name-based and type signature-based searches.
+
+## Search query syntax
+
+```text
+ident = *(ALPHA / DIGIT / "_")
+path = ident *(DOUBLE-COLON ident) [!]
+slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
+arg = [type-filter *WS COLON *WS] (path [generics] / slice / [!])
+type-sep = COMMA/WS *(COMMA/WS)
+nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
+generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
+            CLOSE-ANGLE-BRACKET
+return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
+
+exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
+type-search = [ nonempty-arg-list ] [ return-args ]
+
+query = *WS (exact-search / type-search) *WS
+
+type-filter = (
+    "mod" /
+    "externcrate" /
+    "import" /
+    "struct" /
+    "enum" /
+    "fn" /
+    "type" /
+    "static" /
+    "trait" /
+    "impl" /
+    "tymethod" /
+    "method" /
+    "structfield" /
+    "variant" /
+    "macro" /
+    "primitive" /
+    "associatedtype" /
+    "constant" /
+    "associatedconstant" /
+    "union" /
+    "foreigntype" /
+    "keyword" /
+    "existential" /
+    "attr" /
+    "derive" /
+    "traitalias" /
+    "generic")
+
+OPEN-ANGLE-BRACKET = "<"
+CLOSE-ANGLE-BRACKET = ">"
+OPEN-SQUARE-BRACKET = "["
+CLOSE-SQUARE-BRACKET = "]"
+COLON = ":"
+DOUBLE-COLON = "::"
+QUOTE = %x22
+COMMA = ","
+RETURN-ARROW = "->"
+
+ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+DIGIT = %x30-39
+WS = %x09 / " "
+```
diff --git a/src/doc/style-guide/src/README.md b/src/doc/style-guide/src/README.md
index f4d75967370..9a59d80556e 100644
--- a/src/doc/style-guide/src/README.md
+++ b/src/doc/style-guide/src/README.md
@@ -32,6 +32,19 @@ This should not be interpreted as forbidding developers from following a
 non-default style, or forbidding tools from adding any particular configuration
 options.
 
+## Bugs
+
+If the style guide differs from rustfmt, that may represent a bug in rustfmt,
+or a bug in the style guide; either way, please report it to the style team or
+the rustfmt team or both, for investigation and fix.
+
+If implementing a new formatting tool based on the style guide and default Rust
+style, please test it on the corpus of existing Rust code, and avoid causing
+widespread breakage. The implementation and testing of such a tool may surface
+bugs in either the style guide or rustfmt, as well as bugs in the tool itself.
+
+We typically resolve bugs in a fashion that avoids widespread breakage.
+
 ## Formatting conventions
 
 ### Indentation and line width
diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md
index e9ef509e6a2..12037b5992e 100644
--- a/src/doc/style-guide/src/expressions.md
+++ b/src/doc/style-guide/src/expressions.md
@@ -328,6 +328,26 @@ foo_bar
 Prefer line-breaking at an assignment operator (either `=` or `+=`, etc.) rather
 than at other binary operators.
 
+### Casts (`as`)
+
+Format `as` casts like a binary operator. In particular, always include spaces
+around `as`, and if line-breaking, break before the `as` (never after) and
+block-indent the subsequent line. Format the type on the right-hand side using
+the rules for types.
+
+However, unlike with other binary operators, if chaining a series of `as` casts
+that require line-breaking, and line-breaking before the first `as` suffices to
+make the remainder fit on the next line, don't break before any subsequent
+`as`; instead, leave the series of types all on the same line:
+
+```rust
+let cstr = very_long_expression()
+    as *const str as *const [u8] as *const std::os::raw::c_char;
+```
+
+If the subsequent line still requires line-breaking, break and block-indent
+before each `as` as with other binary operators.
+
 ## Control flow
 
 Do not include extraneous parentheses for `if` and `while` expressions.
@@ -426,14 +446,6 @@ assert_eq!(
 );
 ```
 
-## Casts (`as`)
-
-Put spaces before and after `as`:
-
-```rust
-let cstr = "Hi\0" as *const str as *const [u8] as *const std::os::raw::c_char;
-```
-
 ## Chains of fields and method calls
 
 A chain is a sequence of field accesses, method calls, and/or uses of the try
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index b276745f317..ef7794cc41e 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1639,10 +1639,6 @@ impl Type {
         matches!(self, Type::Generic(_))
     }
 
-    pub(crate) fn is_impl_trait(&self) -> bool {
-        matches!(self, Type::ImplTrait(_))
-    }
-
     pub(crate) fn is_unit(&self) -> bool {
         matches!(self, Type::Tuple(v) if v.is_empty())
     }
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 7696ea0f449..e680b451421 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -16,7 +16,6 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
 use rustc_metadata::rendered_const;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt};
 use rustc_span::symbol::{kw, sym, Symbol};
 use std::fmt::Write as _;
@@ -154,7 +153,7 @@ pub(super) fn external_path<'tcx>(
     args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
 ) -> Path {
     let def_kind = cx.tcx.def_kind(did);
-    let name = cx.tcx.item_name(did);
+    let name = cx.tcx.opt_item_name(did).unwrap_or(kw::Empty);
     Path {
         res: Res::Def(def_kind, did),
         segments: thin_vec![PathSegment {
@@ -282,8 +281,8 @@ pub(crate) fn print_evaluated_const(
         let ty = tcx.type_of(def_id).instantiate_identity();
         match (val, ty.kind()) {
             (_, &ty::Ref(..)) => None,
-            (ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
-            (ConstValue::Scalar(_), _) => {
+            (mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
+            (mir::ConstValue::Scalar(_), _) => {
                 let const_ = mir::ConstantKind::from_value(val, ty);
                 Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
             }
@@ -326,14 +325,14 @@ fn print_const_with_custom_print_scalar<'tcx>(
     // Use a slightly different format for integer types which always shows the actual value.
     // For all other types, fallback to the original `pretty_print_const`.
     match (ct, ct.ty().kind()) {
-        (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => {
+        (mir::ConstantKind::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => {
             if underscores_and_type {
                 format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
             } else {
                 int.to_string()
             }
         }
-        (mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => {
+        (mir::ConstantKind::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => {
             let ty = ct.ty();
             let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
             let data = int.assert_bits(size);
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 59958fbaef9..6dbf2185e3d 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -868,7 +868,7 @@ impl<'tcx> ExtraInfo<'tcx> {
 
 #[derive(Eq, PartialEq, Clone, Debug)]
 pub(crate) struct LangString {
-    original: String,
+    pub(crate) original: String,
     pub(crate) should_panic: bool,
     pub(crate) no_run: bool,
     pub(crate) ignore: Ignore,
@@ -893,11 +893,13 @@ pub(crate) enum Ignore {
 /// ```eBNF
 /// lang-string = *(token-list / delimited-attribute-list / comment)
 ///
-/// bareword = CHAR *(CHAR)
+/// bareword = LEADINGCHAR *(CHAR)
+/// bareword-without-leading-char = CHAR *(CHAR)
 /// quoted-string = QUOTE *(NONQUOTE) QUOTE
 /// token = bareword / quoted-string
+/// token-without-leading-char = bareword-without-leading-char / quoted-string
 /// sep = COMMA/WS *(COMMA/WS)
-/// attribute = (DOT token)/(token EQUAL token)
+/// attribute = (DOT token)/(token EQUAL token-without-leading-char)
 /// attribute-list = [sep] attribute *(sep attribute) [sep]
 /// delimited-attribute-list = OPEN-CURLY-BRACKET attribute-list CLOSE-CURLY-BRACKET
 /// token-list = [sep] token *(sep token) [sep]
@@ -907,8 +909,15 @@ pub(crate) enum Ignore {
 /// CLOSE_PARENT = ")"
 /// OPEN-CURLY-BRACKET = "{"
 /// CLOSE-CURLY-BRACKET = "}"
-/// CHAR = ALPHA / DIGIT / "_" / "-" / ":"
-/// QUOTE = %x22
+/// LEADINGCHAR = ALPHA | DIGIT | "_" | "-" | ":"
+/// ; All ASCII punctuation except comma, quote, equals, backslash, grave (backquote) and braces.
+/// ; Comma is used to separate language tokens, so it can't be used in one.
+/// ; Quote is used to allow otherwise-disallowed characters in language tokens.
+/// ; Equals is used to make key=value pairs in attribute blocks.
+/// ; Backslash and grave are special Markdown characters.
+/// ; Braces are used to start an attribute block.
+/// CHAR = ALPHA | DIGIT | "_" | "-" | ":" | "." | "!" | "#" | "$" | "%" | "&" | "*" | "+" | "/" |
+///        ";" | "<" | ">" | "?" | "@" | "^" | "|" | "~"
 /// NONQUOTE = %x09 / %x20 / %x21 / %x23-7E ; TAB / SPACE / all printable characters except `"`
 /// COMMA = ","
 /// DOT = "."
@@ -932,9 +941,12 @@ pub(crate) enum LangStringToken<'a> {
     KeyValueAttribute(&'a str, &'a str),
 }
 
-fn is_bareword_char(c: char) -> bool {
+fn is_leading_char(c: char) -> bool {
     c == '_' || c == '-' || c == ':' || c.is_ascii_alphabetic() || c.is_ascii_digit()
 }
+fn is_bareword_char(c: char) -> bool {
+    is_leading_char(c) || ".!#$%&*+/;<>?@^|~".contains(c)
+}
 fn is_separator(c: char) -> bool {
     c == ' ' || c == ',' || c == '\t'
 }
@@ -1077,7 +1089,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
                 return self.next();
             } else if c == '.' {
                 return self.parse_class(pos);
-            } else if c == '"' || is_bareword_char(c) {
+            } else if c == '"' || is_leading_char(c) {
                 return self.parse_key_value(c, pos);
             } else {
                 self.emit_error(format!("unexpected character `{c}`"));
@@ -1107,7 +1119,11 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
                     return None;
                 }
                 let indices = self.parse_string(pos)?;
-                if let Some((_, c)) = self.inner.peek().copied() && c != '{' && !is_separator(c) && c != '(' {
+                if let Some((_, c)) = self.inner.peek().copied() &&
+                    c != '{' &&
+                    !is_separator(c) &&
+                    c != '('
+                {
                     self.emit_error(format!("expected ` `, `{{` or `,` after `\"`, found `{c}`"));
                     return None;
                 }
@@ -1115,8 +1131,6 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
             } else if c == '{' {
                 self.is_in_attribute_block = true;
                 return self.next();
-            } else if is_bareword_char(c) {
-                continue;
             } else if is_separator(c) {
                 if pos != start {
                     return Some(LangStringToken::LangToken(&self.data[start..pos]));
@@ -1130,6 +1144,10 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
                     return Some(LangStringToken::LangToken(&self.data[start..pos]));
                 }
                 return self.next();
+            } else if pos == start && is_leading_char(c) {
+                continue;
+            } else if pos != start && is_bareword_char(c) {
+                continue;
             } else {
                 self.emit_error(format!("unexpected character `{c}`"));
                 return None;
@@ -1158,6 +1176,29 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> {
     }
 }
 
+fn tokens(string: &str) -> impl Iterator<Item = LangStringToken<'_>> {
+    // Pandoc, which Rust once used for generating documentation,
+    // expects lang strings to be surrounded by `{}` and for each token
+    // to be proceeded by a `.`. Since some of these lang strings are still
+    // loose in the wild, we strip a pair of surrounding `{}` from the lang
+    // string and a leading `.` from each token.
+
+    let string = string.trim();
+
+    let first = string.chars().next();
+    let last = string.chars().last();
+
+    let string =
+        if first == Some('{') && last == Some('}') { &string[1..string.len() - 1] } else { string };
+
+    string
+        .split(|c| c == ',' || c == ' ' || c == '\t')
+        .map(str::trim)
+        .map(|token| token.strip_prefix('.').unwrap_or(token))
+        .filter(|token| !token.is_empty())
+        .map(|token| LangStringToken::LangToken(token))
+}
+
 impl Default for LangString {
     fn default() -> Self {
         Self {
@@ -1208,122 +1249,130 @@ impl LangString {
 
         data.original = string.to_owned();
 
-        for token in TagIterator::new(string, extra) {
-            match token {
-                LangStringToken::LangToken("should_panic") => {
-                    data.should_panic = true;
-                    seen_rust_tags = !seen_other_tags;
-                }
-                LangStringToken::LangToken("no_run") => {
-                    data.no_run = true;
-                    seen_rust_tags = !seen_other_tags;
-                }
-                LangStringToken::LangToken("ignore") => {
-                    data.ignore = Ignore::All;
-                    seen_rust_tags = !seen_other_tags;
-                }
-                LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
-                    if enable_per_target_ignores {
-                        ignores.push(x.trim_start_matches("ignore-").to_owned());
+        let mut call = |tokens: &mut dyn Iterator<Item = LangStringToken<'_>>| {
+            for token in tokens {
+                match token {
+                    LangStringToken::LangToken("should_panic") => {
+                        data.should_panic = true;
                         seen_rust_tags = !seen_other_tags;
                     }
-                }
-                LangStringToken::LangToken("rust") => {
-                    data.rust = true;
-                    seen_rust_tags = true;
-                }
-                LangStringToken::LangToken("custom") => {
-                    if custom_code_classes_in_docs {
-                        seen_custom_tag = true;
-                    } else {
-                        seen_other_tags = true;
+                    LangStringToken::LangToken("no_run") => {
+                        data.no_run = true;
+                        seen_rust_tags = !seen_other_tags;
                     }
-                }
-                LangStringToken::LangToken("test_harness") => {
-                    data.test_harness = true;
-                    seen_rust_tags = !seen_other_tags || seen_rust_tags;
-                }
-                LangStringToken::LangToken("compile_fail") => {
-                    data.compile_fail = true;
-                    seen_rust_tags = !seen_other_tags || seen_rust_tags;
-                    data.no_run = true;
-                }
-                LangStringToken::LangToken(x) if x.starts_with("edition") => {
-                    data.edition = x[7..].parse::<Edition>().ok();
-                }
-                LangStringToken::LangToken(x)
-                    if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
-                {
-                    if x[1..].parse::<u32>().is_ok() {
-                        data.error_codes.push(x.to_owned());
+                    LangStringToken::LangToken("ignore") => {
+                        data.ignore = Ignore::All;
+                        seen_rust_tags = !seen_other_tags;
+                    }
+                    LangStringToken::LangToken(x) if x.starts_with("ignore-") => {
+                        if enable_per_target_ignores {
+                            ignores.push(x.trim_start_matches("ignore-").to_owned());
+                            seen_rust_tags = !seen_other_tags;
+                        }
+                    }
+                    LangStringToken::LangToken("rust") => {
+                        data.rust = true;
+                        seen_rust_tags = true;
+                    }
+                    LangStringToken::LangToken("custom") => {
+                        if custom_code_classes_in_docs {
+                            seen_custom_tag = true;
+                        } else {
+                            seen_other_tags = true;
+                        }
+                    }
+                    LangStringToken::LangToken("test_harness") => {
+                        data.test_harness = true;
                         seen_rust_tags = !seen_other_tags || seen_rust_tags;
-                    } else {
-                        seen_other_tags = true;
                     }
-                }
-                LangStringToken::LangToken(x) if extra.is_some() => {
-                    let s = x.to_lowercase();
-                    if let Some((flag, help)) = if s == "compile-fail"
-                        || s == "compile_fail"
-                        || s == "compilefail"
+                    LangStringToken::LangToken("compile_fail") => {
+                        data.compile_fail = true;
+                        seen_rust_tags = !seen_other_tags || seen_rust_tags;
+                        data.no_run = true;
+                    }
+                    LangStringToken::LangToken(x) if x.starts_with("edition") => {
+                        data.edition = x[7..].parse::<Edition>().ok();
+                    }
+                    LangStringToken::LangToken(x)
+                        if allow_error_code_check && x.starts_with('E') && x.len() == 5 =>
                     {
-                        Some((
-                            "compile_fail",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or won't fail if it compiles successfully",
-                        ))
-                    } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
-                        Some((
-                            "should_panic",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or won't fail if it doesn't panic when running",
-                        ))
-                    } else if s == "no-run" || s == "no_run" || s == "norun" {
-                        Some((
-                            "no_run",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or will be run (which you might not want)",
-                        ))
-                    } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
-                        Some((
-                            "test_harness",
-                            "the code block will either not be tested if not marked as a rust one \
-                             or the code will be wrapped inside a main function",
-                        ))
-                    } else {
-                        None
-                    } {
-                        if let Some(extra) = extra {
-                            extra.error_invalid_codeblock_attr_with_help(
-                                format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
-                                help,
-                            );
+                        if x[1..].parse::<u32>().is_ok() {
+                            data.error_codes.push(x.to_owned());
+                            seen_rust_tags = !seen_other_tags || seen_rust_tags;
+                        } else {
+                            seen_other_tags = true;
                         }
                     }
-                    seen_other_tags = true;
-                    data.unknown.push(x.to_owned());
-                }
-                LangStringToken::LangToken(x) => {
-                    seen_other_tags = true;
-                    data.unknown.push(x.to_owned());
-                }
-                LangStringToken::KeyValueAttribute(key, value) => {
-                    if custom_code_classes_in_docs {
-                        if key == "class" {
-                            data.added_classes.push(value.to_owned());
-                        } else if let Some(extra) = extra {
-                            extra.error_invalid_codeblock_attr(format!(
-                                "unsupported attribute `{key}`"
-                            ));
+                    LangStringToken::LangToken(x) if extra.is_some() => {
+                        let s = x.to_lowercase();
+                        if let Some((flag, help)) = if s == "compile-fail"
+                            || s == "compile_fail"
+                            || s == "compilefail"
+                        {
+                            Some((
+                                "compile_fail",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or won't fail if it compiles successfully",
+                            ))
+                        } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" {
+                            Some((
+                                "should_panic",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or won't fail if it doesn't panic when running",
+                            ))
+                        } else if s == "no-run" || s == "no_run" || s == "norun" {
+                            Some((
+                                "no_run",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or will be run (which you might not want)",
+                            ))
+                        } else if s == "test-harness" || s == "test_harness" || s == "testharness" {
+                            Some((
+                                "test_harness",
+                                "the code block will either not be tested if not marked as a rust one \
+                                 or the code will be wrapped inside a main function",
+                            ))
+                        } else {
+                            None
+                        } {
+                            if let Some(extra) = extra {
+                                extra.error_invalid_codeblock_attr_with_help(
+                                    format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
+                                    help,
+                                );
+                            }
                         }
-                    } else {
                         seen_other_tags = true;
+                        data.unknown.push(x.to_owned());
+                    }
+                    LangStringToken::LangToken(x) => {
+                        seen_other_tags = true;
+                        data.unknown.push(x.to_owned());
+                    }
+                    LangStringToken::KeyValueAttribute(key, value) => {
+                        if custom_code_classes_in_docs {
+                            if key == "class" {
+                                data.added_classes.push(value.to_owned());
+                            } else if let Some(extra) = extra {
+                                extra.error_invalid_codeblock_attr(format!(
+                                    "unsupported attribute `{key}`"
+                                ));
+                            }
+                        } else {
+                            seen_other_tags = true;
+                        }
+                    }
+                    LangStringToken::ClassAttribute(class) => {
+                        data.added_classes.push(class.to_owned());
                     }
-                }
-                LangStringToken::ClassAttribute(class) => {
-                    data.added_classes.push(class.to_owned());
                 }
             }
+        };
+
+        if custom_code_classes_in_docs {
+            call(&mut TagIterator::new(string, extra).into_iter())
+        } else {
+            call(&mut tokens(string))
         }
 
         // ignore-foo overrides ignore
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index 32957ac57fa..5eba1d0609f 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -226,13 +226,28 @@ fn test_lang_string_parse() {
         ..Default::default()
     });
     // error
-    t(LangString { original: "{.first.second}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: "{.first.second}".into(),
+        rust: true,
+        added_classes: vec!["first.second".into()],
+        ..Default::default()
+    });
     // error
     t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() });
     // error
-    t(LangString { original: "{class=first.second}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: "{class=first.second}".into(),
+        rust: true,
+        added_classes: vec!["first.second".into()],
+        ..Default::default()
+    });
     // error
-    t(LangString { original: "{class=.first}".into(), rust: true, ..Default::default() });
+    t(LangString {
+        original: "{class=.first}".into(),
+        added_classes: vec![".first".into()],
+        rust: true,
+        ..Default::default()
+    });
     t(LangString {
         original: r#"{class="first"}"#.into(),
         added_classes: vec!["first".into()],
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index f70f59d3be3..3e671a64b54 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -101,7 +101,7 @@ pub(crate) struct IndexItem {
     pub(crate) path: String,
     pub(crate) desc: String,
     pub(crate) parent: Option<DefId>,
-    pub(crate) parent_idx: Option<usize>,
+    pub(crate) parent_idx: Option<isize>,
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
     pub(crate) deprecation: Option<Deprecation>,
@@ -122,7 +122,10 @@ impl Serialize for RenderType {
         let id = match &self.id {
             // 0 is a sentinel, everything else is one-indexed
             None => 0,
-            Some(RenderTypeId::Index(idx)) => idx + 1,
+            // concrete type
+            Some(RenderTypeId::Index(idx)) if *idx >= 0 => idx + 1,
+            // generic type parameter
+            Some(RenderTypeId::Index(idx)) => *idx,
             _ => panic!("must convert render types to indexes before serializing"),
         };
         if let Some(generics) = &self.generics {
@@ -140,7 +143,7 @@ impl Serialize for RenderType {
 pub(crate) enum RenderTypeId {
     DefId(DefId),
     Primitive(clean::PrimitiveType),
-    Index(usize),
+    Index(isize),
 }
 
 /// Full type of functions/methods in the search index.
@@ -148,6 +151,7 @@ pub(crate) enum RenderTypeId {
 pub(crate) struct IndexItemFunctionType {
     inputs: Vec<RenderType>,
     output: Vec<RenderType>,
+    where_clause: Vec<Vec<RenderType>>,
 }
 
 impl Serialize for IndexItemFunctionType {
@@ -170,10 +174,17 @@ impl Serialize for IndexItemFunctionType {
                 _ => seq.serialize_element(&self.inputs)?,
             }
             match &self.output[..] {
-                [] => {}
+                [] if self.where_clause.is_empty() => {}
                 [one] if one.generics.is_none() => seq.serialize_element(one)?,
                 _ => seq.serialize_element(&self.output)?,
             }
+            for constraint in &self.where_clause {
+                if let [one] = &constraint[..] && one.generics.is_none() {
+                    seq.serialize_element(one)?;
+                } else {
+                    seq.serialize_element(constraint)?;
+                }
+            }
             seq.end()
         }
     }
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 145c7d18dd0..78c443b2257 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -68,16 +68,16 @@ pub(crate) fn build_index<'tcx>(
     // Reduce `DefId` in paths into smaller sequential numbers,
     // and prune the paths that do not appear in the index.
     let mut lastpath = "";
-    let mut lastpathid = 0usize;
+    let mut lastpathid = 0isize;
 
     // First, on function signatures
     let mut search_index = std::mem::replace(&mut cache.search_index, Vec::new());
     for item in search_index.iter_mut() {
         fn insert_into_map<F: std::hash::Hash + Eq>(
             ty: &mut RenderType,
-            map: &mut FxHashMap<F, usize>,
+            map: &mut FxHashMap<F, isize>,
             itemid: F,
-            lastpathid: &mut usize,
+            lastpathid: &mut isize,
             crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
             item_type: ItemType,
             path: &[Symbol],
@@ -97,9 +97,9 @@ pub(crate) fn build_index<'tcx>(
         fn convert_render_type(
             ty: &mut RenderType,
             cache: &mut Cache,
-            itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
-            primitives: &mut FxHashMap<Symbol, usize>,
-            lastpathid: &mut usize,
+            itemid_to_pathid: &mut FxHashMap<ItemId, isize>,
+            primitives: &mut FxHashMap<Symbol, isize>,
+            lastpathid: &mut isize,
             crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
         ) {
             if let Some(generics) = &mut ty.generics {
@@ -173,6 +173,18 @@ pub(crate) fn build_index<'tcx>(
                     &mut crate_paths,
                 );
             }
+            for constraint in &mut search_type.where_clause {
+                for trait_ in &mut constraint[..] {
+                    convert_render_type(
+                        trait_,
+                        cache,
+                        &mut itemid_to_pathid,
+                        &mut primitives,
+                        &mut lastpathid,
+                        &mut crate_paths,
+                    );
+                }
+            }
         }
     }
 
@@ -402,7 +414,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
     impl_generics: Option<&(clean::Type, clean::Generics)>,
     cache: &Cache,
 ) -> Option<IndexItemFunctionType> {
-    let (mut inputs, mut output) = match *item.kind {
+    let (mut inputs, mut output, where_clause) = match *item.kind {
         clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
         clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
         clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
@@ -412,7 +424,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
     inputs.retain(|a| a.id.is_some() || a.generics.is_some());
     output.retain(|a| a.id.is_some() || a.generics.is_some());
 
-    Some(IndexItemFunctionType { inputs, output })
+    Some(IndexItemFunctionType { inputs, output, where_clause })
 }
 
 fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
@@ -432,96 +444,48 @@ fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
         clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
             get_index_type_id(type_)
         }
-        // The type parameters are converted to generics in `add_generics_and_bounds_as_types`
+        // The type parameters are converted to generics in `simplify_fn_type`
         clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
         clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
+        clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
         // Not supported yet
         clean::BareFunction(_)
         | clean::Generic(_)
         | clean::ImplTrait(_)
-        | clean::Tuple(_)
         | clean::QPath { .. }
         | clean::Infer => None,
     }
 }
 
-/// The point of this function is to replace bounds with types.
+#[derive(Clone, Copy, Eq, Hash, PartialEq)]
+enum SimplifiedParam {
+    // other kinds of type parameters are identified by their name
+    Symbol(Symbol),
+    // every argument-position impl trait is its own type parameter
+    Anonymous(isize),
+}
+
+/// The point of this function is to lower generics and types into the simplified form that the
+/// frontend search engine can use.
 ///
-/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
-/// `[Display, Option]`. If a type parameter has no trait bound, it is discarded.
+/// For example, `[T, U, i32]]` where you have the bounds: `T: Display, U: Option<T>` will return
+/// `[-1, -2, i32] where -1: Display, -2: Option<-1>`. If a type parameter has no traid bound, it
+/// will still get a number. If a constraint is present but not used in the actual types, it will
+/// not be added to the map.
 ///
-/// Important note: It goes through generics recursively. So if you have
-/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
-#[instrument(level = "trace", skip(tcx, res, cache))]
-fn add_generics_and_bounds_as_types<'tcx, 'a>(
+/// This function also works recursively.
+#[instrument(level = "trace", skip(tcx, res, rgen, cache))]
+fn simplify_fn_type<'tcx, 'a>(
     self_: Option<&'a Type>,
     generics: &Generics,
     arg: &'a Type,
     tcx: TyCtxt<'tcx>,
     recurse: usize,
     res: &mut Vec<RenderType>,
+    rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+    is_return: bool,
     cache: &Cache,
 ) {
-    fn insert_ty(res: &mut Vec<RenderType>, ty: Type, mut generics: Vec<RenderType>) {
-        // generics and impl trait are both identified by their generics,
-        // rather than a type name itself
-        let anonymous = ty.is_full_generic() || ty.is_impl_trait();
-        let generics_empty = generics.is_empty();
-
-        if anonymous {
-            if generics_empty {
-                // This is a type parameter with no trait bounds (for example: `T` in
-                // `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
-                // with an empty type with an empty name. Let's just discard it.
-                return;
-            } else if generics.len() == 1 {
-                // In this case, no need to go through an intermediate state if the type parameter
-                // contains only one trait bound.
-                //
-                // For example:
-                //
-                // `fn foo<T: Display>(r: Option<T>) {}`
-                //
-                // In this case, it would contain:
-                //
-                // ```
-                // [{
-                //     name: "option",
-                //     generics: [{
-                //         name: "",
-                //         generics: [
-                //             name: "Display",
-                //             generics: []
-                //         }]
-                //     }]
-                // }]
-                // ```
-                //
-                // After removing the intermediate (unnecessary) type parameter, it'll become:
-                //
-                // ```
-                // [{
-                //     name: "option",
-                //     generics: [{
-                //         name: "Display",
-                //         generics: []
-                //     }]
-                // }]
-                // ```
-                //
-                // To be noted that it can work if there is ONLY ONE trait bound, otherwise we still
-                // need to keep it as is!
-                res.push(generics.pop().unwrap());
-                return;
-            }
-        }
-        let index_ty = get_index_type(&ty, generics);
-        if index_ty.id.is_none() && generics_empty {
-            return;
-        }
-        res.push(index_ty);
-    }
-
     if recurse >= 10 {
         // FIXME: remove this whole recurse thing when the recursion bug is fixed
         // See #59502 for the original issue.
@@ -548,88 +512,126 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
     // for its bounds.
     if let Type::Generic(arg_s) = *arg {
         // First we check if the bounds are in a `where` predicate...
+        let mut type_bounds = Vec::new();
         for where_pred in generics.where_predicates.iter().filter(|g| match g {
             WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
             _ => false,
         }) {
-            let mut ty_generics = Vec::new();
             let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
             for bound in bounds.iter() {
                 if let Some(path) = bound.get_trait_path() {
                     let ty = Type::Path { path };
-                    add_generics_and_bounds_as_types(
+                    simplify_fn_type(
                         self_,
                         generics,
                         &ty,
                         tcx,
                         recurse + 1,
-                        &mut ty_generics,
+                        &mut type_bounds,
+                        rgen,
+                        is_return,
                         cache,
                     );
                 }
             }
-            insert_ty(res, arg.clone(), ty_generics);
         }
         // Otherwise we check if the trait bounds are "inlined" like `T: Option<u32>`...
         if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
-            let mut ty_generics = Vec::new();
             for bound in bound.get_bounds().unwrap_or(&[]) {
                 if let Some(path) = bound.get_trait_path() {
                     let ty = Type::Path { path };
-                    add_generics_and_bounds_as_types(
+                    simplify_fn_type(
                         self_,
                         generics,
                         &ty,
                         tcx,
                         recurse + 1,
-                        &mut ty_generics,
+                        &mut type_bounds,
+                        rgen,
+                        is_return,
                         cache,
                     );
                 }
             }
-            insert_ty(res, arg.clone(), ty_generics);
+        }
+        if let Some((idx, _)) = rgen.get(&SimplifiedParam::Symbol(arg_s)) {
+            res.push(RenderType { id: Some(RenderTypeId::Index(*idx)), generics: None });
+        } else {
+            let idx = -isize::try_from(rgen.len() + 1).unwrap();
+            rgen.insert(SimplifiedParam::Symbol(arg_s), (idx, type_bounds));
+            res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
         }
     } else if let Type::ImplTrait(ref bounds) = *arg {
-        let mut ty_generics = Vec::new();
+        let mut type_bounds = Vec::new();
         for bound in bounds {
             if let Some(path) = bound.get_trait_path() {
                 let ty = Type::Path { path };
-                add_generics_and_bounds_as_types(
+                simplify_fn_type(
                     self_,
                     generics,
                     &ty,
                     tcx,
                     recurse + 1,
-                    &mut ty_generics,
+                    &mut type_bounds,
+                    rgen,
+                    is_return,
                     cache,
                 );
             }
         }
-        insert_ty(res, arg.clone(), ty_generics);
+        if is_return && !type_bounds.is_empty() {
+            // In parameter position, `impl Trait` is a unique thing.
+            res.push(RenderType { id: None, generics: Some(type_bounds) });
+        } else {
+            // In parameter position, `impl Trait` is the same as an unnamed generic parameter.
+            let idx = -isize::try_from(rgen.len() + 1).unwrap();
+            rgen.insert(SimplifiedParam::Anonymous(idx), (idx, type_bounds));
+            res.push(RenderType { id: Some(RenderTypeId::Index(idx)), generics: None });
+        }
     } else if let Type::Slice(ref ty) = *arg {
         let mut ty_generics = Vec::new();
-        add_generics_and_bounds_as_types(
+        simplify_fn_type(
             self_,
             generics,
             &ty,
             tcx,
             recurse + 1,
             &mut ty_generics,
+            rgen,
+            is_return,
             cache,
         );
-        insert_ty(res, arg.clone(), ty_generics);
+        res.push(get_index_type(arg, ty_generics));
     } else if let Type::Array(ref ty, _) = *arg {
         let mut ty_generics = Vec::new();
-        add_generics_and_bounds_as_types(
+        simplify_fn_type(
             self_,
             generics,
             &ty,
             tcx,
             recurse + 1,
             &mut ty_generics,
+            rgen,
+            is_return,
             cache,
         );
-        insert_ty(res, arg.clone(), ty_generics);
+        res.push(get_index_type(arg, ty_generics));
+    } else if let Type::Tuple(ref tys) = *arg {
+        let mut ty_generics = Vec::new();
+        for ty in tys {
+            simplify_fn_type(
+                self_,
+                generics,
+                &ty,
+                tcx,
+                recurse + 1,
+                &mut ty_generics,
+                rgen,
+                is_return,
+                cache,
+            );
+        }
+        res.push(get_index_type(arg, ty_generics));
     } else {
         // This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
         // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -639,18 +641,26 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
         let mut ty_generics = Vec::new();
         if let Some(arg_generics) = arg.generics() {
             for gen in arg_generics.iter() {
-                add_generics_and_bounds_as_types(
+                simplify_fn_type(
                     self_,
                     generics,
                     gen,
                     tcx,
                     recurse + 1,
                     &mut ty_generics,
+                    rgen,
+                    is_return,
                     cache,
                 );
             }
         }
-        insert_ty(res, arg.clone(), ty_generics);
+        let id = get_index_type_id(&arg);
+        if id.is_some() || !ty_generics.is_empty() {
+            res.push(RenderType {
+                id,
+                generics: if ty_generics.is_empty() { None } else { Some(ty_generics) },
+            });
+        }
     }
 }
 
@@ -663,7 +673,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_generics: Option<&(clean::Type, clean::Generics)>,
     cache: &Cache,
-) -> (Vec<RenderType>, Vec<RenderType>) {
+) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
     let decl = &func.decl;
 
     let combined_generics;
@@ -689,21 +699,27 @@ fn get_fn_inputs_and_outputs<'tcx>(
         (None, &func.generics)
     };
 
-    let mut all_types = Vec::new();
+    let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+
+    let mut arg_types = Vec::new();
     for arg in decl.inputs.values.iter() {
-        let mut args = Vec::new();
-        add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
-        if !args.is_empty() {
-            all_types.extend(args);
-        } else {
-            all_types.push(get_index_type(&arg.type_, vec![]));
-        }
+        simplify_fn_type(
+            self_,
+            generics,
+            &arg.type_,
+            tcx,
+            0,
+            &mut arg_types,
+            &mut rgen,
+            false,
+            cache,
+        );
     }
 
     let mut ret_types = Vec::new();
-    add_generics_and_bounds_as_types(self_, generics, &decl.output, tcx, 0, &mut ret_types, cache);
-    if ret_types.is_empty() {
-        ret_types.push(get_index_type(&decl.output, vec![]));
-    }
-    (all_types, ret_types)
+    simplify_fn_type(self_, generics, &decl.output, tcx, 0, &mut ret_types, &mut rgen, true, cache);
+
+    let mut simplified_params = rgen.into_values().collect::<Vec<_>>();
+    simplified_params.sort_by_key(|(idx, _)| -idx);
+    (arg_types, ret_types, simplified_params.into_iter().map(|(_idx, traits)| traits).collect())
 }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 3b236641337..47f9e650281 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -1355,6 +1355,7 @@ a.tooltip:hover::after {
 
 #search-tabs .count {
 	font-size: 1rem;
+	font-variant-numeric: tabular-nums;
 	color: var(--search-tab-title-count-color);
 }
 
@@ -1637,6 +1638,13 @@ However, it's not needed with smaller screen width because the doc/code block is
 
 /* Media Queries */
 
+/* Make sure all the buttons line wrap at the same time */
+@media (max-width: 850px) {
+	#search-tabs .count {
+		display: block;
+	}
+}
+
 /*
 WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
 If you update this line, then you also need to update the line with the same warning
@@ -1764,10 +1772,6 @@ in src-script.js
 		display: none !important;
 	}
 
-	#search-tabs .count {
-		display: block;
-	}
-
 	#main-content > details.toggle > summary::before,
 	#main-content > div > details.toggle > summary::before {
 		left: -11px;
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index f697abd0776..c7811b43d16 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -9,7 +9,7 @@ function initSearch(searchIndex){}
 /**
  * @typedef {{
  *     name: string,
- *     id: integer,
+ *     id: integer|null,
  *     fullPath: Array<string>,
  *     pathWithoutLast: Array<string>,
  *     pathLast: string,
@@ -37,6 +37,7 @@ let ParserState;
  *     args: Array<QueryElement>,
  *     returned: Array<QueryElement>,
  *     foundElems: number,
+ *     totalElems: number,
  *     literalSearch: boolean,
  *     corrections: Array<{from: string, to: integer}>,
  * }}
@@ -103,7 +104,7 @@ let ResultObject;
  *
  *     fn something() -> Result<usize, usize>
  *
- * If output was allowed to be any RawFunctionType, it would look like this
+ * If output was allowed to be any RawFunctionType, it would look like thi
  *
  *     [[], [50, [3, 3]]]
  *
@@ -113,10 +114,56 @@ let ResultObject;
  * in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
  * is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
  *
+ * The output can be skipped if it's actually unit and there's no type constraints. If thi
+ * function accepts constrained generics, then the output will be unconditionally emitted, and
+ * after it will come a list of trait constraints. The position of the item in the list will
+ * determine which type parameter it is. For example:
+ *
+ *     [1, 2, 3, 4, 5]
+ *      ^  ^  ^  ^  ^
+ *      |  |  |  |  - generic parameter (-3) of trait 5
+ *      |  |  |  - generic parameter (-2) of trait 4
+ *      |  |  - generic parameter (-1) of trait 3
+ *      |  - this function returns a single value (type 2)
+ *      - this function takes a single input parameter (type 1)
+ *
+ * Or, for a less contrived version:
+ *
+ *     [[[4, -1], 3], [[5, -1]], 11]
+ *      -^^^^^^^----   ^^^^^^^   ^^
+ *       |        |    |          - generic parameter, roughly `where -1: 11`
+ *       |        |    |            since -1 is the type parameter and 11 the trait
+ *       |        |    - function output 5<-1>
+ *       |        - the overall function signature is something like
+ *       |          `fn(4<-1>, 3) -> 5<-1> where -1: 11`
+ *       - function input, corresponds roughly to 4<-1>
+ *         4 is an index into the `p` array for a type
+ *         -1 is the generic parameter, given by 11
+ *
+ * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
+ * function inputs and outputs:
+ *
+ *     [-1, -1, [4, 3]]
+ *              ^^^^^^ where -1: 4 + 3
+ *
+ * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
+ * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
+ * favor of `4 + 3`:
+ *
+ *     [-1, -1, [[4, 3]]]
+ *              ^^^^^^^^ where -1: 4 + 3
+ *
+ *     [-1, -1, [5, [4, 3]]]
+ *              ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
+ *
+ * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
+ * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
+ *
  * @typedef {(
  *     0 |
  *     [(number|Array<RawFunctionType>)] |
- *     [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)]
+ *     [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
+ *     Array<(number|Array<RawFunctionType>)>
  * )}
  */
 let RawFunctionSearchType;
@@ -136,6 +183,7 @@ let RawFunctionType;
  * @typedef {{
  *     inputs: Array<FunctionType>,
  *     output: Array<FunctionType>,
+ *     where_clause: Array<Array<FunctionType>>,
  * }}
  */
 let FunctionSearchType;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 0e270bbcc40..2f0cae0a48e 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -3,6 +3,17 @@
 
 "use strict";
 
+// polyfill
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced
+if (!Array.prototype.toSpliced) {
+    // Can't use arrow functions, because we want `this`
+    Array.prototype.toSpliced = function() {
+        const me = this.slice();
+        Array.prototype.splice.apply(me, arguments);
+        return me;
+    };
+}
+
 (function() {
 // This mapping table should match the discriminants of
 // `rustdoc::formats::item_type::ItemType` type in Rust.
@@ -33,6 +44,7 @@ const itemTypes = [
     "attr",
     "derive",
     "traitalias",
+    "generic",
 ];
 
 const longItemTypes = [
@@ -67,6 +79,7 @@ const longItemTypes = [
 // used for special search precedence
 const TY_PRIMITIVE = itemTypes.indexOf("primitive");
 const TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_GENERIC = itemTypes.indexOf("generic");
 const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
 
 function hasOwnPropertyRustdoc(obj, property) {
@@ -252,7 +265,7 @@ function initSearch(rawSearchIndex) {
 
     /**
      * Add an item to the type Name->ID map, or, if one already exists, use it.
-     * Returns the number. If name is "" or null, return -1 (pure generic).
+     * Returns the number. If name is "" or null, return null (pure generic).
      *
      * This is effectively string interning, so that function matching can be
      * done more quickly. Two types with the same name but different item kinds
@@ -264,7 +277,7 @@ function initSearch(rawSearchIndex) {
      */
     function buildTypeMapIndex(name) {
         if (name === "" || name === null) {
-            return -1;
+            return null;
         }
 
         if (typeNameIdMap.has(name)) {
@@ -489,7 +502,7 @@ function initSearch(rawSearchIndex) {
             }
             return {
                 name: "never",
-                id: -1,
+                id: null,
                 fullPath: ["never"],
                 pathWithoutLast: [],
                 pathLast: "never",
@@ -531,7 +544,7 @@ function initSearch(rawSearchIndex) {
         }
         return {
             name: name.trim(),
-            id: -1,
+            id: null,
             fullPath: pathSegments,
             pathWithoutLast: pathSegments.slice(0, pathSegments.length - 1),
             pathLast: pathSegments[pathSegments.length - 1],
@@ -660,7 +673,7 @@ function initSearch(rawSearchIndex) {
             }
             elems.push({
                 name: "[]",
-                id: -1,
+                id: null,
                 fullPath: ["[]"],
                 pathWithoutLast: [],
                 pathLast: "[]",
@@ -971,9 +984,13 @@ function initSearch(rawSearchIndex) {
             returned: [],
             // Total number of "top" elements (does not include generics).
             foundElems: 0,
+            // Total number of elements (includes generics).
+            totalElems: 0,
             literalSearch: false,
             error: null,
             correction: null,
+            proposeCorrectionFrom: null,
+            proposeCorrectionTo: null,
         };
     }
 
@@ -1014,64 +1031,10 @@ function initSearch(rawSearchIndex) {
     /**
      * Parses the query.
      *
-     * The supported syntax by this parser is as follow:
-     *
-     * ident = *(ALPHA / DIGIT / "_")
-     * path = ident *(DOUBLE-COLON/{WS} ident) [!]
-     * slice = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
-     * arg = [type-filter *WS COLON *WS] (path [generics] / slice)
-     * type-sep = *WS COMMA *(COMMA)
-     * nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
-     * generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
-     *            CLOSE-ANGLE-BRACKET
-     * return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
-     *
-     * exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
-     * type-search = [ nonempty-arg-list ] [ return-args ]
-     *
-     * query = *WS (exact-search / type-search) *WS
-     *
-     * type-filter = (
-     *     "mod" /
-     *     "externcrate" /
-     *     "import" /
-     *     "struct" /
-     *     "enum" /
-     *     "fn" /
-     *     "type" /
-     *     "static" /
-     *     "trait" /
-     *     "impl" /
-     *     "tymethod" /
-     *     "method" /
-     *     "structfield" /
-     *     "variant" /
-     *     "macro" /
-     *     "primitive" /
-     *     "associatedtype" /
-     *     "constant" /
-     *     "associatedconstant" /
-     *     "union" /
-     *     "foreigntype" /
-     *     "keyword" /
-     *     "existential" /
-     *     "attr" /
-     *     "derive" /
-     *     "traitalias")
+     * The supported syntax by this parser is given in the rustdoc book chapter
+     * /src/doc/rustdoc/src/read-documentation/search.md
      *
-     * OPEN-ANGLE-BRACKET = "<"
-     * CLOSE-ANGLE-BRACKET = ">"
-     * OPEN-SQUARE-BRACKET = "["
-     * CLOSE-SQUARE-BRACKET = "]"
-     * COLON = ":"
-     * DOUBLE-COLON = "::"
-     * QUOTE = %x22
-     * COMMA = ","
-     * RETURN-ARROW = "->"
-     *
-     * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
-     * DIGIT = %x30-39
-     * WS = %x09 / " "
+     * When adding new things to the parser, add them there, too!
      *
      * @param  {string} val     - The user query
      *
@@ -1124,6 +1087,7 @@ function initSearch(rawSearchIndex) {
             query.literalSearch = parserState.totalElems > 1;
         }
         query.foundElems = query.elems.length + query.returned.length;
+        query.totalElems = parserState.totalElems;
         return query;
     }
 
@@ -1172,7 +1136,7 @@ function initSearch(rawSearchIndex) {
             const out = [];
 
             for (const result of results) {
-                if (result.id > -1) {
+                if (result.id !== -1) {
                     const obj = searchIndex[result.id];
                     obj.dist = result.dist;
                     const res = buildHrefAndPath(obj);
@@ -1348,192 +1312,311 @@ function initSearch(rawSearchIndex) {
          * This function checks generics in search query `queryElem` can all be found in the
          * search index (`fnType`),
          *
-         * @param {FunctionType} fnType     - The object to check.
-         * @param {QueryElement} queryElem  - The element from the parsed query.
+         * This function returns `true` if it matches, and also writes the results to mgensInout.
+         * It returns `false` if no match is found, and leaves mgensInout untouched.
+         *
+         * @param {FunctionType} fnType                - The object to check.
+         * @param {QueryElement} queryElem             - The element from the parsed query.
+         * @param {[FunctionType]} whereClause         - Trait bounds for generic items.
+         * @param {Map<number,number>|null} mgensInout - Map functions generics to query generics.
          *
          * @return {boolean} - Returns true if a match, false otherwise.
          */
-        function checkGenerics(fnType, queryElem) {
-            return unifyFunctionTypes(fnType.generics, queryElem.generics);
+        function checkGenerics(fnType, queryElem, whereClause, mgensInout) {
+            return unifyFunctionTypes(
+                fnType.generics,
+                queryElem.generics,
+                whereClause,
+                mgensInout,
+                mgens => {
+                    if (mgensInout) {
+                        for (const [fid, qid] of mgens.entries()) {
+                            mgensInout.set(fid, qid);
+                        }
+                    }
+                    return true;
+                }
+            );
         }
         /**
          * This function checks if a list of search query `queryElems` can all be found in the
          * search index (`fnTypes`).
          *
-         * @param {Array<FunctionType>} fnTypes    - The objects to check.
+         * This function returns `true` on a match, or `false` if none. If `solutionCb` is
+         * supplied, it will call that function with mgens, and that callback can accept or
+         * reject the result bu returning `true` or `false`. If the callback returns false,
+         * then this function will try with a different solution, or bail with false if it
+         * runs out of candidates.
+         *
+         * @param {Array<FunctionType>} fnTypes - The objects to check.
          * @param {Array<QueryElement>} queryElems - The elements from the parsed query.
+         * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+         * @param {Map<number,number>|null} mgensIn
+         *     - Map functions generics to query generics (never modified).
+         * @param {null|Map<number,number> -> bool} solutionCb - Called for each `mgens` solution.
          *
          * @return {boolean} - Returns true if a match, false otherwise.
          */
-        function unifyFunctionTypes(fnTypes, queryElems) {
-            // This search engine implements order-agnostic unification. There
-            // should be no missing duplicates (generics have "bag semantics"),
-            // and the row is allowed to have extras.
+        function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) {
+            /**
+             * @type Map<integer, integer>
+             */
+            let mgens = new Map(mgensIn);
             if (queryElems.length === 0) {
-                return true;
+                return !solutionCb || solutionCb(mgens);
             }
-            if (!fnTypes || fnTypes.length === 0) {
+            if (!fnTypesIn || fnTypesIn.length === 0) {
                 return false;
             }
+            const ql = queryElems.length;
+            let fl = fnTypesIn.length;
             /**
-             * @type Map<integer, QueryElement[]>
+             * @type Array<FunctionType>
              */
-            const queryElemSet = new Map();
-            const addQueryElemToQueryElemSet = queryElem => {
-                let currentQueryElemList;
-                if (queryElemSet.has(queryElem.id)) {
-                    currentQueryElemList = queryElemSet.get(queryElem.id);
-                } else {
-                    currentQueryElemList = [];
-                    queryElemSet.set(queryElem.id, currentQueryElemList);
-                }
-                currentQueryElemList.push(queryElem);
-            };
-            for (const queryElem of queryElems) {
-                addQueryElemToQueryElemSet(queryElem);
-            }
+            let fnTypes = fnTypesIn.slice();
             /**
-             * @type Map<integer, FunctionType[]>
+             * loop works by building up a solution set in the working arrays
+             * fnTypes gets mutated in place to make this work, while queryElems
+             * is left alone
+             *
+             *                                  vvvvvvv `i` points here
+             * queryElems = [ good, good, good, unknown, unknown ],
+             * fnTypes    = [ good, good, good, unknown, unknown ],
+             *                ----------------  ^^^^^^^^^^^^^^^^ `j` iterates after `i`,
+             *                |                                   looking for candidates
+             *                everything before `i` is the
+             *                current working solution
+             *
+             * Everything in the current working solution is known to be a good
+             * match, but it might not be the match we wind up going with, because
+             * there might be more than one candidate match, and we need to try them all
+             * before giving up. So, to handle this, it backtracks on failure.
+             *
+             * @type Array<{
+             *     "fnTypesScratch": Array<FunctionType>,
+             *     "queryElemsOffset": integer,
+             *     "fnTypesOffset": integer
+             * }>
              */
-            const fnTypeSet = new Map();
-            const addFnTypeToFnTypeSet = fnType => {
-                // Pure generic, or an item that's not matched by any query elems.
-                // Try [unboxing] it.
-                //
-                // [unboxing]:
-                // http://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
-                const queryContainsArrayOrSliceElem = queryElemSet.has(typeNameIdOfArrayOrSlice);
-                if (fnType.id === -1 || !(
-                    queryElemSet.has(fnType.id) ||
-                    (fnType.id === typeNameIdOfSlice && queryContainsArrayOrSliceElem) ||
-                    (fnType.id === typeNameIdOfArray && queryContainsArrayOrSliceElem)
-                )) {
-                    for (const innerFnType of fnType.generics) {
-                        addFnTypeToFnTypeSet(innerFnType);
-                    }
-                    return;
-                }
-                let currentQueryElemList = queryElemSet.get(fnType.id) || [];
-                let matchIdx = currentQueryElemList.findIndex(queryElem => {
-                    return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
-                        checkGenerics(fnType, queryElem);
-                });
-                if (matchIdx === -1 &&
-                    (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray) &&
-                    queryContainsArrayOrSliceElem
-                ) {
-                    currentQueryElemList = queryElemSet.get(typeNameIdOfArrayOrSlice) || [];
-                    matchIdx = currentQueryElemList.findIndex(queryElem => {
-                        return typePassesFilter(queryElem.typeFilter, fnType.ty) &&
-                            checkGenerics(fnType, queryElem);
-                    });
-                }
-                // None of the query elems match the function type.
-                // Try [unboxing] it.
-                if (matchIdx === -1) {
-                    for (const innerFnType of fnType.generics) {
-                        addFnTypeToFnTypeSet(innerFnType);
+            const backtracking = [];
+            let i = 0;
+            let j = 0;
+            const backtrack = () => {
+                while (backtracking.length !== 0) {
+                    // this session failed, but there are other possible solutions
+                    // to backtrack, reset to (a copy of) the old array, do the swap or unboxing
+                    const {
+                        fnTypesScratch,
+                        mgensScratch,
+                        queryElemsOffset,
+                        fnTypesOffset,
+                        unbox,
+                    } = backtracking.pop();
+                    mgens = new Map(mgensScratch);
+                    const fnType = fnTypesScratch[fnTypesOffset];
+                    const queryElem = queryElems[queryElemsOffset];
+                    if (unbox) {
+                        if (fnType.id < 0) {
+                            if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+                                continue;
+                            }
+                            mgens.set(fnType.id, 0);
+                        }
+                        const generics = fnType.id < 0 ?
+                            whereClause[(-fnType.id) - 1] :
+                            fnType.generics;
+                        fnTypes = fnTypesScratch.toSpliced(fnTypesOffset, 1, ...generics);
+                        fl = fnTypes.length;
+                        // re-run the matching algorithm on this item
+                        i = queryElemsOffset - 1;
+                    } else {
+                        if (fnType.id < 0) {
+                            if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+                                continue;
+                            }
+                            mgens.set(fnType.id, queryElem.id);
+                        }
+                        fnTypes = fnTypesScratch.slice();
+                        fl = fnTypes.length;
+                        const tmp = fnTypes[queryElemsOffset];
+                        fnTypes[queryElemsOffset] = fnTypes[fnTypesOffset];
+                        fnTypes[fnTypesOffset] = tmp;
+                        // this is known as a good match; go to the next one
+                        i = queryElemsOffset;
                     }
-                    return;
-                }
-                let currentFnTypeList;
-                if (fnTypeSet.has(fnType.id)) {
-                    currentFnTypeList = fnTypeSet.get(fnType.id);
-                } else {
-                    currentFnTypeList = [];
-                    fnTypeSet.set(fnType.id, currentFnTypeList);
-                }
-                currentFnTypeList.push(fnType);
-            };
-            for (const fnType of fnTypes) {
-                addFnTypeToFnTypeSet(fnType);
-            }
-            const doHandleQueryElemList = (currentFnTypeList, queryElemList) => {
-                if (queryElemList.length === 0) {
                     return true;
                 }
-                // Multiple items in one list might match multiple items in another.
-                // Since an item with fewer generics can match an item with more, we
-                // need to check all combinations for a potential match.
-                const queryElem = queryElemList.pop();
-                const l = currentFnTypeList.length;
-                for (let i = 0; i < l; i += 1) {
-                    const fnType = currentFnTypeList[i];
-                    if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
-                        continue;
-                    }
-                    const queryElemPathLength = queryElem.pathWithoutLast.length;
-                    // If the query element is a path (it contains `::`), we need to check if this
-                    // path is compatible with the target type.
-                    if (queryElemPathLength > 0) {
-                        const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
-                            fnType.path.split("::") : [];
-                        // If the path provided in the query element is longer than this type,
-                        // no need to check it since it won't match in any case.
-                        if (queryElemPathLength > fnTypePath.length) {
-                            continue;
+                return false;
+            };
+            for (i = 0; i !== ql; ++i) {
+                const queryElem = queryElems[i];
+                /**
+                 * list of potential function types that go with the current query element.
+                 * @type Array<integer>
+                 */
+                const matchCandidates = [];
+                let fnTypesScratch = null;
+                let mgensScratch = null;
+                // don't try anything before `i`, because they've already been
+                // paired off with the other query elements
+                for (j = i; j !== fl; ++j) {
+                    const fnType = fnTypes[j];
+                    if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) {
+                        if (!fnTypesScratch) {
+                            fnTypesScratch = fnTypes.slice();
                         }
-                        let i = 0;
-                        for (const path of fnTypePath) {
-                            if (path === queryElem.pathWithoutLast[i]) {
-                                i += 1;
-                                if (i >= queryElemPathLength) {
-                                    break;
-                                }
+                        unifyFunctionTypes(
+                            fnType.generics,
+                            queryElem.generics,
+                            whereClause,
+                            mgens,
+                            mgensScratch => {
+                                matchCandidates.push({
+                                    fnTypesScratch,
+                                    mgensScratch,
+                                    queryElemsOffset: i,
+                                    fnTypesOffset: j,
+                                    unbox: false,
+                                });
+                                return false; // "reject" all candidates to gather all of them
                             }
+                        );
+                    }
+                    if (unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) {
+                        if (!fnTypesScratch) {
+                            fnTypesScratch = fnTypes.slice();
                         }
-                        if (i < queryElemPathLength) {
-                            // If we didn't find all parts of the path of the query element inside
-                            // the fn type, then it's not the right one.
-                            continue;
+                        if (!mgensScratch) {
+                            mgensScratch = new Map(mgens);
                         }
+                        backtracking.push({
+                            fnTypesScratch,
+                            mgensScratch,
+                            queryElemsOffset: i,
+                            fnTypesOffset: j,
+                            unbox: true,
+                        });
                     }
-                    if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
-                        currentFnTypeList.splice(i, 1);
-                        const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
-                        if (result) {
-                            return true;
-                        }
-                        currentFnTypeList.splice(i, 0, fnType);
+                }
+                if (matchCandidates.length === 0) {
+                    if (backtrack()) {
+                        continue;
+                    } else {
+                        return false;
                     }
                 }
+                // use the current candidate
+                const {fnTypesOffset: candidate, mgensScratch: mgensNew} = matchCandidates.pop();
+                if (fnTypes[candidate].id < 0 && queryElems[i].id < 0) {
+                    mgens.set(fnTypes[candidate].id, queryElems[i].id);
+                }
+                for (const [fid, qid] of mgensNew) {
+                    mgens.set(fid, qid);
+                }
+                // `i` and `j` are paired off
+                // `queryElems[i]` is left in place
+                // `fnTypes[j]` is swapped with `fnTypes[i]` to pair them off
+                const tmp = fnTypes[candidate];
+                fnTypes[candidate] = fnTypes[i];
+                fnTypes[i] = tmp;
+                // write other candidates to backtracking queue
+                for (const otherCandidate of matchCandidates) {
+                    backtracking.push(otherCandidate);
+                }
+                // If we're on the last item, check the solution with the callback
+                // backtrack if the callback says its unsuitable
+                while (i === (ql - 1) && solutionCb && !solutionCb(mgens)) {
+                    if (!backtrack()) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens) {
+            // type filters look like `trait:Read` or `enum:Result`
+            if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
                 return false;
-            };
-            const handleQueryElemList = (id, queryElemList) => {
-                if (!fnTypeSet.has(id)) {
-                    if (id === typeNameIdOfArrayOrSlice) {
-                        return handleQueryElemList(typeNameIdOfSlice, queryElemList) ||
-                            handleQueryElemList(typeNameIdOfArray, queryElemList);
+            }
+            // fnType.id < 0 means generic
+            // queryElem.id < 0 does too
+            // mgens[fnType.id] = queryElem.id
+            // or, if mgens[fnType.id] = 0, then we've matched this generic with a bare trait
+            // and should make that same decision everywhere it appears
+            if (fnType.id < 0 && queryElem.id < 0) {
+                if (mgens.has(fnType.id) && mgens.get(fnType.id) !== queryElem.id) {
+                    return false;
+                }
+                for (const [fid, qid] of mgens.entries()) {
+                    if (fnType.id !== fid && queryElem.id === qid) {
+                        return false;
                     }
+                    if (fnType.id === fid && queryElem.id !== qid) {
+                        return false;
+                    }
+                }
+            } else if (fnType.id !== null) {
+                if (queryElem.id === typeNameIdOfArrayOrSlice &&
+                    (fnType.id === typeNameIdOfSlice || fnType.id === typeNameIdOfArray)
+                ) {
+                    // [] matches primitive:array or primitive:slice
+                    // if it matches, then we're fine, and this is an appropriate match candidate
+                } else if (fnType.id !== queryElem.id) {
                     return false;
                 }
-                const currentFnTypeList = fnTypeSet.get(id);
-                if (currentFnTypeList.length < queryElemList.length) {
-                    // It's not possible for all the query elems to find a match.
+                // If the query elem has generics, and the function doesn't,
+                // it can't match.
+                if (fnType.generics.length === 0 && queryElem.generics.length !== 0) {
                     return false;
                 }
-                const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
-                if (result) {
-                    // Found a solution.
-                    // Any items that weren't used for it can be unboxed, and might form
-                    // part of the solution for another item.
-                    for (const innerFnType of currentFnTypeList) {
-                        addFnTypeToFnTypeSet(innerFnType);
+                // If the query element is a path (it contains `::`), we need to check if this
+                // path is compatible with the target type.
+                const queryElemPathLength = queryElem.pathWithoutLast.length;
+                if (queryElemPathLength > 0) {
+                    const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
+                        fnType.path.split("::") : [];
+                    // If the path provided in the query element is longer than this type,
+                    // no need to check it since it won't match in any case.
+                    if (queryElemPathLength > fnTypePath.length) {
+                        return false;
                     }
-                    fnTypeSet.delete(id);
-                }
-                return result;
-            };
-            let queryElemSetSize = -1;
-            while (queryElemSetSize !== queryElemSet.size) {
-                queryElemSetSize = queryElemSet.size;
-                for (const [id, queryElemList] of queryElemSet) {
-                    if (handleQueryElemList(id, queryElemList)) {
-                        queryElemSet.delete(id);
+                    let i = 0;
+                    for (const path of fnTypePath) {
+                        if (path === queryElem.pathWithoutLast[i]) {
+                            i += 1;
+                            if (i >= queryElemPathLength) {
+                                break;
+                            }
+                        }
+                    }
+                    if (i < queryElemPathLength) {
+                        // If we didn't find all parts of the path of the query element inside
+                        // the fn type, then it's not the right one.
+                        return false;
                     }
                 }
             }
-            return queryElemSetSize === 0;
+            return true;
+        }
+        function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) {
+            if (fnType.id < 0 && queryElem.id >= 0) {
+                if (!whereClause) {
+                    return false;
+                }
+                // mgens[fnType.id] === 0 indicates that we committed to unboxing this generic
+                // mgens[fnType.id] === null indicates that we haven't decided yet
+                if (mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
+                    return false;
+                }
+                // This is only a potential unbox if the search query appears in the where clause
+                // for example, searching `Read -> usize` should find
+                // `fn read_all<R: Read>(R) -> Result<usize>`
+                // generic `R` is considered "unboxed"
+                return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
+            } else if (fnType.generics && fnType.generics.length > 0) {
+                return checkIfInList(fnType.generics, queryElem, whereClause);
+            }
+            return false;
         }
 
         /**
@@ -1541,13 +1624,14 @@ function initSearch(rawSearchIndex) {
           * generics (if any).
           *
           * @param {Array<FunctionType>} list
-          * @param {QueryElement} elem    - The element from the parsed query.
+          * @param {QueryElement} elem          - The element from the parsed query.
+          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
           *
           * @return {boolean} - Returns true if found, false otherwise.
           */
-        function checkIfInList(list, elem) {
+        function checkIfInList(list, elem, whereClause) {
             for (const entry of list) {
-                if (checkType(entry, elem)) {
+                if (checkType(entry, elem, whereClause)) {
                     return true;
                 }
             }
@@ -1559,14 +1643,26 @@ function initSearch(rawSearchIndex) {
           * generics (if any).
           *
           * @param {Row} row
-          * @param {QueryElement} elem      - The element from the parsed query.
+          * @param {QueryElement} elem          - The element from the parsed query.
+          * @param {[FunctionType]} whereClause - Trait bounds for generic items.
           *
           * @return {boolean} - Returns true if the type matches, false otherwise.
           */
-        function checkType(row, elem) {
-            if (row.id === -1) {
+        function checkType(row, elem, whereClause) {
+            if (row.id === null) {
                 // This is a pure "generic" search, no need to run other checks.
-                return row.generics.length > 0 ? checkIfInList(row.generics, elem) : false;
+                return row.generics.length > 0
+                    ? checkIfInList(row.generics, elem, whereClause)
+                    : false;
+            }
+
+            if (row.id < 0 && elem.id >= 0) {
+                const gid = (-row.id) - 1;
+                return checkIfInList(whereClause[gid], elem, whereClause);
+            }
+
+            if (row.id < 0 && elem.id < 0) {
+                return true;
             }
 
             const matchesExact = row.id === elem.id;
@@ -1576,7 +1672,7 @@ function initSearch(rawSearchIndex) {
             if ((matchesExact || matchesArrayOrSlice) &&
                 typePassesFilter(elem.typeFilter, row.ty)) {
                 if (elem.generics.length > 0) {
-                    return checkGenerics(row, elem);
+                    return checkGenerics(row, elem, whereClause, new Map());
                 }
                 return true;
             }
@@ -1584,7 +1680,7 @@ function initSearch(rawSearchIndex) {
             // If the current item does not match, try [unboxing] the generic.
             // [unboxing]:
             //   https://ndmitchell.com/downloads/slides-hoogle_fast_type_searching-09_aug_2008.pdf
-            return checkIfInList(row.generics, elem);
+            return checkIfInList(row.generics, elem, whereClause);
         }
 
         function checkPath(contains, ty, maxEditDistance) {
@@ -1785,13 +1881,15 @@ function initSearch(rawSearchIndex) {
             const fullId = row.id;
             const searchWord = searchWords[pos];
 
-            const in_args = row.type && row.type.inputs && checkIfInList(row.type.inputs, elem);
+            const in_args = row.type && row.type.inputs
+                && checkIfInList(row.type.inputs, elem, row.type.where_clause);
             if (in_args) {
                 // path_dist is 0 because no parent path information is currently stored
                 // in the search index
                 addIntoResults(results_in_args, fullId, pos, -1, 0, 0, maxEditDistance);
             }
-            const returned = row.type && row.type.output && checkIfInList(row.type.output, elem);
+            const returned = row.type && row.type.output
+                && checkIfInList(row.type.output, elem, row.type.where_clause);
             if (returned) {
                 addIntoResults(results_returned, fullId, pos, -1, 0, 0, maxEditDistance);
             }
@@ -1853,10 +1951,20 @@ function initSearch(rawSearchIndex) {
             }
 
             // If the result is too "bad", we return false and it ends this search.
-            if (!unifyFunctionTypes(row.type.inputs, parsedQuery.elems)) {
-                return;
-            }
-            if (!unifyFunctionTypes(row.type.output, parsedQuery.returned)) {
+            if (!unifyFunctionTypes(
+                row.type.inputs,
+                parsedQuery.elems,
+                row.type.where_clause,
+                null,
+                mgens => {
+                    return unifyFunctionTypes(
+                        row.type.output,
+                        parsedQuery.returned,
+                        row.type.where_clause,
+                        mgens
+                    );
+                }
+            )) {
                 return;
             }
 
@@ -1876,6 +1984,11 @@ function initSearch(rawSearchIndex) {
             const maxEditDistance = Math.floor(queryLen / 3);
 
             /**
+             * @type {Map<string, integer>}
+             */
+            const genericSymbols = new Map();
+
+            /**
              * Convert names to ids in parsed query elements.
              * This is not used for the "In Names" tab, but is used for the
              * "In Params", "In Returns", and "In Function Signature" tabs.
@@ -1891,7 +2004,7 @@ function initSearch(rawSearchIndex) {
                 if (typeNameIdMap.has(elem.pathLast)) {
                     elem.id = typeNameIdMap.get(elem.pathLast);
                 } else if (!parsedQuery.literalSearch) {
-                    let match = -1;
+                    let match = null;
                     let matchDist = maxEditDistance + 1;
                     let matchName = "";
                     for (const [name, id] of typeNameIdMap) {
@@ -1905,11 +2018,52 @@ function initSearch(rawSearchIndex) {
                             matchName = name;
                         }
                     }
-                    if (match !== -1) {
+                    if (match !== null) {
                         parsedQuery.correction = matchName;
                     }
                     elem.id = match;
                 }
+                if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1
+                     && elem.generics.length === 0)
+                    || elem.typeFilter === TY_GENERIC) {
+                    if (genericSymbols.has(elem.name)) {
+                        elem.id = genericSymbols.get(elem.name);
+                    } else {
+                        elem.id = -(genericSymbols.size + 1);
+                        genericSymbols.set(elem.name, elem.id);
+                    }
+                    if (elem.typeFilter === -1 && elem.name.length >= 3) {
+                        // Silly heuristic to catch if the user probably meant
+                        // to not write a generic parameter. We don't use it,
+                        // just bring it up.
+                        const maxPartDistance = Math.floor(elem.name.length / 3);
+                        let matchDist = maxPartDistance + 1;
+                        let matchName = "";
+                        for (const name of typeNameIdMap.keys()) {
+                            const dist = editDistance(name, elem.name, maxPartDistance);
+                            if (dist <= matchDist && dist <= maxPartDistance) {
+                                if (dist === matchDist && matchName > name) {
+                                    continue;
+                                }
+                                matchDist = dist;
+                                matchName = name;
+                            }
+                        }
+                        if (matchName !== "") {
+                            parsedQuery.proposeCorrectionFrom = elem.name;
+                            parsedQuery.proposeCorrectionTo = matchName;
+                        }
+                    }
+                    elem.typeFilter = TY_GENERIC;
+                }
+                if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) {
+                    // Rust does not have HKT
+                    parsedQuery.error = [
+                        "Generic type parameter ",
+                        elem.name,
+                        " does not accept generic parameters",
+                    ];
+                }
                 for (const elem2 of elem.generics) {
                     convertNameToId(elem2);
                 }
@@ -1943,8 +2097,11 @@ function initSearch(rawSearchIndex) {
                     elem = parsedQuery.returned[0];
                     for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
                         row = searchIndex[i];
-                        in_returned = row.type &&
-                            unifyFunctionTypes(row.type.output, parsedQuery.returned);
+                        in_returned = row.type && unifyFunctionTypes(
+                            row.type.output,
+                            parsedQuery.returned,
+                            row.type.where_clause
+                        );
                         if (in_returned) {
                             addIntoResults(
                                 results_others,
@@ -2177,11 +2334,20 @@ ${item.displayPath}<span class="${type}">${name}</span>\
     }
 
     function makeTabHeader(tabNb, text, nbElems) {
+        // https://blog.horizon-eda.org/misc/2020/02/19/ui.html
+        //
+        // CSS runs with `font-variant-numeric: tabular-nums` to ensure all
+        // digits are the same width. \u{2007} is a Unicode space character
+        // that is defined to be the same width as a digit.
+        const fmtNbElems =
+            nbElems < 10  ? `\u{2007}(${nbElems})\u{2007}\u{2007}` :
+            nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` :
+            `\u{2007}(${nbElems})`;
         if (searchState.currentTab === tabNb) {
             return "<button class=\"selected\">" + text +
-                   " <span class=\"count\">(" + nbElems + ")</span></button>";
+                   "<span class=\"count\">" + fmtNbElems + "</span></button>";
         }
-        return "<button>" + text + " <span class=\"count\">(" + nbElems + ")</span></button>";
+        return "<button>" + text + "<span class=\"count\">" + fmtNbElems + "</span></button>";
     }
 
     /**
@@ -2295,6 +2461,13 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 "Showing results for closest type name " +
                 `"${results.query.correction}" instead.</h3>`;
         }
+        if (results.query.proposeCorrectionFrom !== null) {
+            const orig = results.query.proposeCorrectionFrom;
+            const targ = results.query.proposeCorrectionTo;
+            output += "<h3 class=\"search-corrections\">" +
+                `Type "${orig}" not found and used as generic parameter. ` +
+                `Consider searching for "${targ}" instead.</h3>`;
+        }
 
         const resultsElem = document.createElement("div");
         resultsElem.id = "results";
@@ -2396,37 +2569,54 @@ ${item.displayPath}<span class="${type}">${name}</span>\
      * @return {Array<FunctionSearchType>}
      */
     function buildItemSearchTypeAll(types, lowercasePaths) {
+        return types.map(type => buildItemSearchType(type, lowercasePaths));
+    }
+
+    /**
+     * Converts a single type.
+     *
+     * @param {RawFunctionType} type
+     */
+    function buildItemSearchType(type, lowercasePaths) {
         const PATH_INDEX_DATA = 0;
         const GENERICS_DATA = 1;
-        return types.map(type => {
-            let pathIndex, generics;
-            if (typeof type === "number") {
-                pathIndex = type;
-                generics = [];
-            } else {
-                pathIndex = type[PATH_INDEX_DATA];
-                generics = buildItemSearchTypeAll(
-                    type[GENERICS_DATA],
-                    lowercasePaths
-                );
-            }
+        let pathIndex, generics;
+        if (typeof type === "number") {
+            pathIndex = type;
+            generics = [];
+        } else {
+            pathIndex = type[PATH_INDEX_DATA];
+            generics = buildItemSearchTypeAll(
+                type[GENERICS_DATA],
+                lowercasePaths
+            );
+        }
+        if (pathIndex < 0) {
+            // types less than 0 are generic parameters
+            // the actual names of generic parameters aren't stored, since they aren't API
+            return {
+                id: pathIndex,
+                ty: TY_GENERIC,
+                path: null,
+                generics,
+            };
+        }
+        if (pathIndex === 0) {
             // `0` is used as a sentinel because it's fewer bytes than `null`
-            if (pathIndex === 0) {
-                return {
-                    id: -1,
-                    ty: null,
-                    path: null,
-                    generics: generics,
-                };
-            }
-            const item = lowercasePaths[pathIndex - 1];
             return {
-                id: buildTypeMapIndex(item.name),
-                ty: item.ty,
-                path: item.path,
-                generics: generics,
+                id: null,
+                ty: null,
+                path: null,
+                generics,
             };
-        });
+        }
+        const item = lowercasePaths[pathIndex - 1];
+        return {
+            id: buildTypeMapIndex(item.name),
+            ty: item.ty,
+            path: item.path,
+            generics,
+        };
     }
 
     /**
@@ -2454,23 +2644,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         }
         let inputs, output;
         if (typeof functionSearchType[INPUTS_DATA] === "number") {
-            const pathIndex = functionSearchType[INPUTS_DATA];
-            if (pathIndex === 0) {
-                inputs = [{
-                    id: -1,
-                    ty: null,
-                    path: null,
-                    generics: [],
-                }];
-            } else {
-                const item = lowercasePaths[pathIndex - 1];
-                inputs = [{
-                    id: buildTypeMapIndex(item.name),
-                    ty: item.ty,
-                    path: item.path,
-                    generics: [],
-                }];
-            }
+            inputs = [buildItemSearchType(functionSearchType[INPUTS_DATA], lowercasePaths)];
         } else {
             inputs = buildItemSearchTypeAll(
                 functionSearchType[INPUTS_DATA],
@@ -2479,23 +2653,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         }
         if (functionSearchType.length > 1) {
             if (typeof functionSearchType[OUTPUT_DATA] === "number") {
-                const pathIndex = functionSearchType[OUTPUT_DATA];
-                if (pathIndex === 0) {
-                    output = [{
-                        id: -1,
-                        ty: null,
-                        path: null,
-                        generics: [],
-                    }];
-                } else {
-                    const item = lowercasePaths[pathIndex - 1];
-                    output = [{
-                        id: buildTypeMapIndex(item.name),
-                        ty: item.ty,
-                        path: item.path,
-                        generics: [],
-                    }];
-                }
+                output = [buildItemSearchType(functionSearchType[OUTPUT_DATA], lowercasePaths)];
             } else {
                 output = buildItemSearchTypeAll(
                     functionSearchType[OUTPUT_DATA],
@@ -2505,8 +2663,15 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         } else {
             output = [];
         }
+        const where_clause = [];
+        const l = functionSearchType.length;
+        for (let i = 2; i < l; ++i) {
+            where_clause.push(typeof functionSearchType[i] === "number"
+                ? [buildItemSearchType(functionSearchType[i], lowercasePaths)]
+                : buildItemSearchTypeAll(functionSearchType[i], lowercasePaths));
+        }
         return {
-            inputs, output,
+            inputs, output, where_clause,
         };
     }
 
diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs
index 1a703a4e967..6266d3ff51d 100644
--- a/src/librustdoc/passes/check_custom_code_classes.rs
+++ b/src/librustdoc/passes/check_custom_code_classes.rs
@@ -21,6 +21,10 @@ pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass {
 };
 
 pub(crate) fn check_custom_code_classes(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+    if cx.tcx.features().custom_code_classes_in_docs {
+        // Nothing to check here if the feature is enabled.
+        return krate;
+    }
     let mut coll = CustomCodeClassLinter { cx };
 
     coll.fold_crate(krate)
@@ -59,7 +63,7 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
     let dox = item.attrs.doc_value();
     find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true);
 
-    if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs {
+    if !tests.custom_classes_found.is_empty() {
         let span = item.attr_span(cx.tcx);
         let sess = &cx.tcx.sess.parse_sess;
         let mut err = sess
diff --git a/src/llvm-project b/src/llvm-project
-Subproject 0537f6354cffe546cbf47f6dc9c7f82e49e86cf
+Subproject 42263494d29febc26d3c1ebdaa7b63677573ec4
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index fcb90c63a6f..c88fce22f7f 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -656,7 +656,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
 }
 
 pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant<'tcx>> {
-    use rustc_middle::mir::interpret::ConstValue;
+    use rustc_middle::mir::ConstValue;
     match result {
         mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
             ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index f0b4ede35fb..9e25d97f5a6 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -13,7 +13,7 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::mir::{ConstValue, interpret::Scalar};
 use rustc_middle::traits::EvaluationResult;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index b91d5a958bb..ba273489eb8 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -141,6 +141,22 @@ impl PanicStrategy {
     }
 }
 
+#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum Sanitizer {
+    Address,
+    Cfi,
+    Kcfi,
+    KernelAddress,
+    Leak,
+    Memory,
+    Memtag,
+    Safestack,
+    ShadowCallStack,
+    Thread,
+    Hwaddress,
+}
+
 /// Configuration for compiletest
 #[derive(Debug, Default, Clone)]
 pub struct Config {
@@ -560,6 +576,10 @@ pub struct TargetCfg {
     pub(crate) panic: PanicStrategy,
     #[serde(default)]
     pub(crate) dynamic_linking: bool,
+    #[serde(rename = "supported-sanitizers", default)]
+    pub(crate) sanitizers: Vec<Sanitizer>,
+    #[serde(rename = "supports-xray", default)]
+    pub(crate) xray: bool,
 }
 
 impl TargetCfg {
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index 62364ede47b..1113721fff6 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -1,6 +1,5 @@
-use crate::common::{Config, Debugger};
+use crate::common::{Config, Debugger, Sanitizer};
 use crate::header::IgnoreDecision;
-use crate::util;
 
 pub(super) fn handle_needs(
     cache: &CachedNeedsConditions,
@@ -220,21 +219,22 @@ impl CachedNeedsConditions {
             path.iter().any(|dir| dir.join("x86_64-w64-mingw32-dlltool").is_file());
 
         let target = &&*config.target;
+        let sanitizers = &config.target_cfg().sanitizers;
         Self {
             sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(),
-            sanitizer_address: util::ASAN_SUPPORTED_TARGETS.contains(target),
-            sanitizer_cfi: util::CFI_SUPPORTED_TARGETS.contains(target),
-            sanitizer_kcfi: util::KCFI_SUPPORTED_TARGETS.contains(target),
-            sanitizer_kasan: util::KASAN_SUPPORTED_TARGETS.contains(target),
-            sanitizer_leak: util::LSAN_SUPPORTED_TARGETS.contains(target),
-            sanitizer_memory: util::MSAN_SUPPORTED_TARGETS.contains(target),
-            sanitizer_thread: util::TSAN_SUPPORTED_TARGETS.contains(target),
-            sanitizer_hwaddress: util::HWASAN_SUPPORTED_TARGETS.contains(target),
-            sanitizer_memtag: util::MEMTAG_SUPPORTED_TARGETS.contains(target),
-            sanitizer_shadow_call_stack: util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(target),
-            sanitizer_safestack: util::SAFESTACK_SUPPORTED_TARGETS.contains(target),
+            sanitizer_address: sanitizers.contains(&Sanitizer::Address),
+            sanitizer_cfi: sanitizers.contains(&Sanitizer::Cfi),
+            sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi),
+            sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress),
+            sanitizer_leak: sanitizers.contains(&Sanitizer::Leak),
+            sanitizer_memory: sanitizers.contains(&Sanitizer::Memory),
+            sanitizer_thread: sanitizers.contains(&Sanitizer::Thread),
+            sanitizer_hwaddress: sanitizers.contains(&Sanitizer::Hwaddress),
+            sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag),
+            sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack),
+            sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack),
             profiler_support: std::env::var_os("RUSTC_PROFILER_SUPPORT").is_some(),
-            xray: util::XRAY_SUPPORTED_TARGETS.contains(target),
+            xray: config.target_cfg().xray,
 
             // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find
             // whether `rust-lld` is present in the compiler under test.
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 362fba11697..2fd80b52cee 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -53,47 +53,117 @@ fn test_parse_normalization_string() {
     assert_eq!(s, r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."#);
 }
 
-fn config() -> Config {
-    let args = &[
-        "compiletest",
-        "--mode=ui",
-        "--suite=ui",
-        "--compile-lib-path=",
-        "--run-lib-path=",
-        "--python=",
-        "--jsondocck-path=",
-        "--src-base=",
-        "--build-base=",
-        "--sysroot-base=",
-        "--stage-id=stage2-x86_64-unknown-linux-gnu",
-        "--cc=c",
-        "--cxx=c++",
-        "--cflags=",
-        "--cxxflags=",
-        "--llvm-components=",
-        "--android-cross-path=",
-        "--target=x86_64-unknown-linux-gnu",
-        "--channel=nightly",
-    ];
-    let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
-    args.push("--rustc-path".to_string());
-    // This is a subtle/fragile thing. On rust-lang CI, there is no global
-    // `rustc`, and Cargo doesn't offer a convenient way to get the path to
-    // `rustc`. Fortunately bootstrap sets `RUSTC` for us, which is pointing
-    // to the stage0 compiler.
-    //
-    // Otherwise, if you are running compiletests's tests manually, you
-    // probably don't have `RUSTC` set, in which case this falls back to the
-    // global rustc. If your global rustc is too far out of sync with stage0,
-    // then this may cause confusing errors. Or if for some reason you don't
-    // have rustc in PATH, that would also fail.
-    args.push(std::env::var("RUSTC").unwrap_or_else(|_| {
-        eprintln!(
-            "warning: RUSTC not set, using global rustc (are you not running via bootstrap?)"
-        );
-        "rustc".to_string()
-    }));
-    crate::parse_config(args)
+#[derive(Default)]
+struct ConfigBuilder {
+    channel: Option<String>,
+    host: Option<String>,
+    target: Option<String>,
+    stage_id: Option<String>,
+    llvm_version: Option<String>,
+    git_hash: bool,
+    system_llvm: bool,
+}
+
+impl ConfigBuilder {
+    fn channel(&mut self, s: &str) -> &mut Self {
+        self.channel = Some(s.to_owned());
+        self
+    }
+
+    fn host(&mut self, s: &str) -> &mut Self {
+        self.host = Some(s.to_owned());
+        self
+    }
+
+    fn target(&mut self, s: &str) -> &mut Self {
+        self.target = Some(s.to_owned());
+        self
+    }
+
+    fn stage_id(&mut self, s: &str) -> &mut Self {
+        self.stage_id = Some(s.to_owned());
+        self
+    }
+
+    fn llvm_version(&mut self, s: &str) -> &mut Self {
+        self.llvm_version = Some(s.to_owned());
+        self
+    }
+
+    fn git_hash(&mut self, b: bool) -> &mut Self {
+        self.git_hash = b;
+        self
+    }
+
+    fn system_llvm(&mut self, s: bool) -> &mut Self {
+        self.system_llvm = s;
+        self
+    }
+
+    fn build(&mut self) -> Config {
+        let args = &[
+            "compiletest",
+            "--mode=ui",
+            "--suite=ui",
+            "--compile-lib-path=",
+            "--run-lib-path=",
+            "--python=",
+            "--jsondocck-path=",
+            "--src-base=",
+            "--build-base=",
+            "--sysroot-base=",
+            "--cc=c",
+            "--cxx=c++",
+            "--cflags=",
+            "--cxxflags=",
+            "--llvm-components=",
+            "--android-cross-path=",
+            "--stage-id",
+            self.stage_id.as_deref().unwrap_or("stage2-x86_64-unknown-linux-gnu"),
+            "--channel",
+            self.channel.as_deref().unwrap_or("nightly"),
+            "--host",
+            self.host.as_deref().unwrap_or("x86_64-unknown-linux-gnu"),
+            "--target",
+            self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"),
+        ];
+        let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
+
+        if let Some(ref llvm_version) = self.llvm_version {
+            args.push("--llvm-version".to_owned());
+            args.push(llvm_version.clone());
+        }
+
+        if self.git_hash {
+            args.push("--git-hash".to_owned());
+        }
+        if self.system_llvm {
+            args.push("--system-llvm".to_owned());
+        }
+
+        args.push("--rustc-path".to_string());
+        // This is a subtle/fragile thing. On rust-lang CI, there is no global
+        // `rustc`, and Cargo doesn't offer a convenient way to get the path to
+        // `rustc`. Fortunately bootstrap sets `RUSTC` for us, which is pointing
+        // to the stage0 compiler.
+        //
+        // Otherwise, if you are running compiletests's tests manually, you
+        // probably don't have `RUSTC` set, in which case this falls back to the
+        // global rustc. If your global rustc is too far out of sync with stage0,
+        // then this may cause confusing errors. Or if for some reason you don't
+        // have rustc in PATH, that would also fail.
+        args.push(std::env::var("RUSTC").unwrap_or_else(|_| {
+            eprintln!(
+                "warning: RUSTC not set, using global rustc (are you not running via bootstrap?)"
+            );
+            "rustc".to_string()
+        }));
+        crate::parse_config(args)
+    }
+}
+
+fn cfg() -> ConfigBuilder {
+    ConfigBuilder::default()
 }
 
 fn parse_rs(config: &Config, contents: &str) -> EarlyProps {
@@ -115,7 +185,7 @@ fn parse_makefile(config: &Config, contents: &str) -> EarlyProps {
 
 #[test]
 fn should_fail() {
-    let config = config();
+    let config: Config = cfg().build();
     let tn = test::DynTestName(String::new());
     let p = Path::new("a.rs");
 
@@ -127,7 +197,7 @@ fn should_fail() {
 
 #[test]
 fn revisions() {
-    let config = config();
+    let config: Config = cfg().build();
 
     assert_eq!(parse_rs(&config, "// revisions: a b c").revisions, vec!["a", "b", "c"],);
     assert_eq!(
@@ -138,7 +208,7 @@ fn revisions() {
 
 #[test]
 fn aux_build() {
-    let config = config();
+    let config: Config = cfg().build();
 
     assert_eq!(
         parse_rs(
@@ -155,36 +225,31 @@ fn aux_build() {
 
 #[test]
 fn no_system_llvm() {
-    let mut config = config();
-
-    config.system_llvm = false;
+    let config: Config = cfg().system_llvm(false).build();
     assert!(!check_ignore(&config, "// no-system-llvm"));
 
-    config.system_llvm = true;
+    let config: Config = cfg().system_llvm(true).build();
     assert!(check_ignore(&config, "// no-system-llvm"));
 }
 
 #[test]
 fn llvm_version() {
-    let mut config = config();
-
-    config.llvm_version = Some(80102);
+    let config: Config = cfg().llvm_version("8.1.2").build();
     assert!(check_ignore(&config, "// min-llvm-version: 9.0"));
 
-    config.llvm_version = Some(90001);
+    let config: Config = cfg().llvm_version("9.0.1").build();
     assert!(check_ignore(&config, "// min-llvm-version: 9.2"));
 
-    config.llvm_version = Some(90301);
+    let config: Config = cfg().llvm_version("9.3.1").build();
     assert!(!check_ignore(&config, "// min-llvm-version: 9.2"));
 
-    config.llvm_version = Some(100000);
+    let config: Config = cfg().llvm_version("10.0.0").build();
     assert!(!check_ignore(&config, "// min-llvm-version: 9.0"));
 }
 
 #[test]
 fn ignore_target() {
-    let mut config = config();
-    config.target = "x86_64-unknown-linux-gnu".to_owned();
+    let config: Config = cfg().target("x86_64-unknown-linux-gnu").build();
 
     assert!(check_ignore(&config, "// ignore-x86_64-unknown-linux-gnu"));
     assert!(check_ignore(&config, "// ignore-x86_64"));
@@ -200,8 +265,7 @@ fn ignore_target() {
 
 #[test]
 fn only_target() {
-    let mut config = config();
-    config.target = "x86_64-pc-windows-gnu".to_owned();
+    let config: Config = cfg().target("x86_64-pc-windows-gnu").build();
 
     assert!(check_ignore(&config, "// only-x86"));
     assert!(check_ignore(&config, "// only-linux"));
@@ -217,8 +281,7 @@ fn only_target() {
 
 #[test]
 fn stage() {
-    let mut config = config();
-    config.stage_id = "stage1-x86_64-unknown-linux-gnu".to_owned();
+    let config: Config = cfg().stage_id("stage1-x86_64-unknown-linux-gnu").build();
 
     assert!(check_ignore(&config, "// ignore-stage1"));
     assert!(!check_ignore(&config, "// ignore-stage2"));
@@ -226,18 +289,16 @@ fn stage() {
 
 #[test]
 fn cross_compile() {
-    let mut config = config();
-    config.host = "x86_64-apple-darwin".to_owned();
-    config.target = "wasm32-unknown-unknown".to_owned();
+    let config: Config = cfg().host("x86_64-apple-darwin").target("wasm32-unknown-unknown").build();
     assert!(check_ignore(&config, "// ignore-cross-compile"));
 
-    config.target = config.host.clone();
+    let config: Config = cfg().host("x86_64-apple-darwin").target("x86_64-apple-darwin").build();
     assert!(!check_ignore(&config, "// ignore-cross-compile"));
 }
 
 #[test]
 fn debugger() {
-    let mut config = config();
+    let mut config = cfg().build();
     config.debugger = None;
     assert!(!check_ignore(&config, "// ignore-cdb"));
 
@@ -253,27 +314,24 @@ fn debugger() {
 
 #[test]
 fn git_hash() {
-    let mut config = config();
-    config.git_hash = false;
+    let config: Config = cfg().git_hash(false).build();
     assert!(check_ignore(&config, "// needs-git-hash"));
 
-    config.git_hash = true;
+    let config: Config = cfg().git_hash(true).build();
     assert!(!check_ignore(&config, "// needs-git-hash"));
 }
 
 #[test]
 fn sanitizers() {
-    let mut config = config();
-
     // Target that supports all sanitizers:
-    config.target = "x86_64-unknown-linux-gnu".to_owned();
+    let config: Config = cfg().target("x86_64-unknown-linux-gnu").build();
     assert!(!check_ignore(&config, "// needs-sanitizer-address"));
     assert!(!check_ignore(&config, "// needs-sanitizer-leak"));
     assert!(!check_ignore(&config, "// needs-sanitizer-memory"));
     assert!(!check_ignore(&config, "// needs-sanitizer-thread"));
 
     // Target that doesn't support sanitizers:
-    config.target = "wasm32-unknown-emscripten".to_owned();
+    let config: Config = cfg().target("wasm32-unknown-emscripten").build();
     assert!(check_ignore(&config, "// needs-sanitizer-address"));
     assert!(check_ignore(&config, "// needs-sanitizer-leak"));
     assert!(check_ignore(&config, "// needs-sanitizer-memory"));
@@ -291,8 +349,7 @@ fn asm_support() {
         ("i686-unknown-netbsd", true),
     ];
     for (target, has_asm) in asms {
-        let mut config = config();
-        config.target = target.to_string();
+        let config = cfg().target(target).build();
         assert_eq!(config.has_asm_support(), has_asm);
         assert_eq!(check_ignore(&config, "// needs-asm-support"), !has_asm)
     }
@@ -300,8 +357,7 @@ fn asm_support() {
 
 #[test]
 fn channel() {
-    let mut config = config();
-    config.channel = "beta".into();
+    let config: Config = cfg().channel("beta").build();
 
     assert!(check_ignore(&config, "// ignore-beta"));
     assert!(check_ignore(&config, "// only-nightly"));
@@ -330,7 +386,7 @@ fn test_extract_version_range() {
 #[test]
 #[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
 fn test_duplicate_revisions() {
-    let config = config();
+    let config: Config = cfg().build();
     parse_rs(&config, "// revisions: rpass1 rpass1");
 }
 
@@ -345,8 +401,7 @@ fn ignore_arch() {
         ("thumbv7m-none-eabi", "thumb"),
     ];
     for (target, arch) in archs {
-        let mut config = config();
-        config.target = target.to_string();
+        let config: Config = cfg().target(target).build();
         assert!(config.matches_arch(arch), "{target} {arch}");
         assert!(check_ignore(&config, &format!("// ignore-{arch}")));
     }
@@ -361,8 +416,7 @@ fn matches_os() {
         ("x86_64-unknown-none", "none"),
     ];
     for (target, os) in oss {
-        let mut config = config();
-        config.target = target.to_string();
+        let config = cfg().target(target).build();
         assert!(config.matches_os(os), "{target} {os}");
         assert!(check_ignore(&config, &format!("// ignore-{os}")));
     }
@@ -376,8 +430,7 @@ fn matches_env() {
         ("arm-unknown-linux-musleabi", "musl"),
     ];
     for (target, env) in envs {
-        let mut config = config();
-        config.target = target.to_string();
+        let config: Config = cfg().target(target).build();
         assert!(config.matches_env(env), "{target} {env}");
         assert!(check_ignore(&config, &format!("// ignore-{env}")));
     }
@@ -391,8 +444,7 @@ fn matches_abi() {
         ("arm-unknown-linux-gnueabi", "eabi"),
     ];
     for (target, abi) in abis {
-        let mut config = config();
-        config.target = target.to_string();
+        let config: Config = cfg().target(target).build();
         assert!(config.matches_abi(abi), "{target} {abi}");
         assert!(check_ignore(&config, &format!("// ignore-{abi}")));
     }
@@ -408,8 +460,7 @@ fn is_big_endian() {
         ("powerpc64-unknown-linux-gnu", true),
     ];
     for (target, is_big) in endians {
-        let mut config = config();
-        config.target = target.to_string();
+        let config = cfg().target(target).build();
         assert_eq!(config.is_big_endian(), is_big, "{target} {is_big}");
         assert_eq!(check_ignore(&config, "// ignore-endian-big"), is_big);
     }
@@ -424,8 +475,7 @@ fn pointer_width() {
         ("msp430-none-elf", 16),
     ];
     for (target, width) in widths {
-        let mut config = config();
-        config.target = target.to_string();
+        let config: Config = cfg().target(target).build();
         assert_eq!(config.get_pointer_width(), width, "{target} {width}");
         assert_eq!(check_ignore(&config, "// ignore-16bit"), width == 16);
         assert_eq!(check_ignore(&config, "// ignore-32bit"), width == 32);
@@ -456,8 +506,7 @@ fn wasm_special() {
         ("wasm64-unknown-unknown", "wasm64", true),
     ];
     for (target, pattern, ignore) in ignores {
-        let mut config = config();
-        config.target = target.to_string();
+        let config: Config = cfg().target(target).build();
         assert_eq!(
             check_ignore(&config, &format!("// ignore-{pattern}")),
             ignore,
@@ -476,8 +525,7 @@ fn families() {
         ("wasm32-unknown-emscripten", "unix"),
     ];
     for (target, family) in families {
-        let mut config = config();
-        config.target = target.to_string();
+        let config: Config = cfg().target(target).build();
         assert!(config.matches_family(family));
         let other = if family == "windows" { "unix" } else { "windows" };
         assert!(!config.matches_family(other));
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 7b42d8e9b58..657d074b380 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2335,14 +2335,17 @@ impl<'test> TestCx<'test> {
         rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
         rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
 
-        // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
-        // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
-        // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
-        // source will be shown, causing a blessing hell.
-        rustc.arg("-Z").arg(format!(
-            "ignore-directory-in-diagnostics-source-blocks={}",
-            home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
-        ));
+        // #[cfg(not(bootstrap))]: After beta bump, this should **always** run.
+        if !(self.config.stage_id.starts_with("stage1-") && self.config.suite == "ui-fulldeps") {
+            // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
+            // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
+            // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
+            // source will be shown, causing a blessing hell.
+            rustc.arg("-Z").arg(format!(
+                "ignore-directory-in-diagnostics-source-blocks={}",
+                home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
+            ));
+        }
 
         // Optionally prevent default --sysroot if specified in test compile-flags.
         if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot"))
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 02648fe5c29..8f9425eb071 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -9,108 +9,6 @@ use tracing::*;
 #[cfg(test)]
 mod tests;
 
-pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[
-    "aarch64-apple-darwin",
-    "aarch64-apple-ios",
-    "aarch64-apple-ios-sim",
-    "aarch64-apple-ios-macabi",
-    "aarch64-unknown-fuchsia",
-    "aarch64-linux-android",
-    "aarch64-unknown-linux-gnu",
-    "arm-linux-androideabi",
-    "armv7-linux-androideabi",
-    "i686-linux-android",
-    "i686-unknown-linux-gnu",
-    "x86_64-apple-darwin",
-    "x86_64-apple-ios",
-    "x86_64-apple-ios-macabi",
-    "x86_64-unknown-fuchsia",
-    "x86_64-linux-android",
-    "x86_64-unknown-freebsd",
-    "x86_64-unknown-linux-gnu",
-    "s390x-unknown-linux-gnu",
-];
-
-// FIXME(rcvalle): More targets are likely supported.
-pub const CFI_SUPPORTED_TARGETS: &[&str] = &[
-    "aarch64-apple-darwin",
-    "aarch64-unknown-fuchsia",
-    "aarch64-linux-android",
-    "aarch64-unknown-freebsd",
-    "aarch64-unknown-linux-gnu",
-    "x86_64-apple-darwin",
-    "x86_64-unknown-fuchsia",
-    "x86_64-pc-solaris",
-    "x86_64-unknown-freebsd",
-    "x86_64-unknown-illumos",
-    "x86_64-unknown-linux-gnu",
-    "x86_64-unknown-linux-musl",
-    "x86_64-unknown-netbsd",
-];
-
-pub const KCFI_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-none", "x86_64-linux-none"];
-
-pub const KASAN_SUPPORTED_TARGETS: &[&str] = &[
-    "aarch64-unknown-none",
-    "riscv64gc-unknown-none-elf",
-    "riscv64imac-unknown-none-elf",
-    "x86_64-unknown-none",
-];
-
-pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
-    // FIXME: currently broken, see #88132
-    // "aarch64-apple-darwin",
-    "aarch64-unknown-linux-gnu",
-    "x86_64-apple-darwin",
-    "x86_64-apple-ios-macabi",
-    "x86_64-unknown-linux-gnu",
-    "s390x-unknown-linux-gnu",
-];
-
-pub const MSAN_SUPPORTED_TARGETS: &[&str] = &[
-    "aarch64-unknown-linux-gnu",
-    "x86_64-unknown-freebsd",
-    "x86_64-unknown-linux-gnu",
-    "s390x-unknown-linux-gnu",
-];
-
-pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[
-    "aarch64-apple-darwin",
-    "aarch64-apple-ios",
-    "aarch64-apple-ios-sim",
-    "aarch64-apple-ios-macabi",
-    "aarch64-unknown-linux-gnu",
-    "x86_64-apple-darwin",
-    "x86_64-apple-ios",
-    "x86_64-apple-ios-macabi",
-    "x86_64-unknown-freebsd",
-    "x86_64-unknown-linux-gnu",
-    "s390x-unknown-linux-gnu",
-];
-
-pub const HWASAN_SUPPORTED_TARGETS: &[&str] =
-    &["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
-
-pub const MEMTAG_SUPPORTED_TARGETS: &[&str] =
-    &["aarch64-linux-android", "aarch64-unknown-linux-gnu"];
-
-pub const SHADOWCALLSTACK_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-android"];
-
-pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[
-    "aarch64-linux-android",
-    "aarch64-unknown-linux-gnu",
-    "aarch64-unknown-linux-musl",
-    "x86_64-linux-android",
-    "x86_64-unknown-freebsd",
-    "x86_64-unknown-linux-gnu",
-    "x86_64-unknown-linux-musl",
-    "x86_64-unknown-netbsd",
-    "x86_64-unknown-none-linuxkernel",
-    "x86_64-unknown-openbsd",
-];
-
-pub const SAFESTACK_SUPPORTED_TARGETS: &[&str] = &["x86_64-unknown-linux-gnu"];
-
 pub fn make_new_path(path: &str) -> String {
     assert!(cfg!(windows));
     // Windows just uses PATH as the library search path, so we have to
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index ca5b6d22515..f253d71e50d 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -33,17 +33,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
 
 [[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
-[[package]]
 name = "autocfg"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -71,6 +60,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
 name = "bstr"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -155,13 +150,13 @@ dependencies = [
 
 [[package]]
 name = "colored"
-version = "2.0.0"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
+checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
 dependencies = [
- "atty",
+ "is-terminal",
  "lazy_static",
- "winapi",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -201,12 +196,12 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
 
 [[package]]
 name = "env_logger"
-version = "0.9.3"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
 dependencies = [
- "atty",
  "humantime",
+ "is-terminal",
  "log",
  "regex",
  "termcolor",
@@ -271,15 +266,6 @@ checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
@@ -311,12 +297,23 @@ version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
 dependencies = [
- "hermit-abi 0.3.1",
+ "hermit-abi",
  "libc",
  "windows-sys 0.48.0",
 ]
 
 [[package]]
+name = "is-terminal"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+dependencies = [
+ "hermit-abi",
+ "rustix 0.38.14",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
 name = "itoa"
 version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -330,9 +327,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.142"
+version = "0.2.148"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
+checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
 
 [[package]]
 name = "libffi"
@@ -370,6 +367,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
 
 [[package]]
+name = "linux-raw-sys"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
+
+[[package]]
 name = "lock_api"
 version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -454,7 +457,7 @@ version = "0.26.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "cfg-if",
  "libc",
  "static_assertions",
@@ -581,7 +584,7 @@ version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
@@ -590,7 +593,7 @@ version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
@@ -655,11 +658,24 @@ version = "0.37.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "errno",
  "io-lifetimes",
  "libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.3.7",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f"
+dependencies = [
+ "bitflags 2.4.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.7",
  "windows-sys 0.48.0",
 ]
 
@@ -756,7 +772,7 @@ dependencies = [
  "cfg-if",
  "fastrand",
  "redox_syscall 0.3.5",
- "rustix",
+ "rustix 0.37.19",
  "windows-sys 0.45.0",
 ]
 
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index 67a2aeefa02..2ae6f922e3a 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -19,7 +19,7 @@ doctest = false # and no doc tests
 
 [dependencies]
 getrandom = { version = "0.2", features = ["std"] }
-env_logger = "0.9"
+env_logger = "0.10"
 log = "0.4"
 rand = "0.8"
 smallvec = "1.7"
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index a2030c1c343..f6db58695fb 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-19dd9535408db0f1ff3d16613619076aef524d19
+4fda889bf8735755573b27e6116ce025f3ded5f9
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 72d9dbd045d..b05087134a0 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -14,7 +14,7 @@ use rustc_middle::mir;
 use rustc_middle::ty::{
     self,
     layout::{IntegerExt as _, LayoutOf, TyAndLayout},
-    Ty, TyCtxt,
+    IntTy, Ty, TyCtxt, UintTy,
 };
 use rustc_span::{def_id::CrateNum, sym, Span, Symbol};
 use rustc_target::abi::{Align, FieldIdx, FieldsShape, Integer, Size, Variants};
@@ -1066,6 +1066,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 ),
         }
     }
+
+    /// Returns an integer type that is twice wide as `ty`
+    fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let this = self.eval_context_ref();
+        match ty.kind() {
+            // Unsigned
+            ty::Uint(UintTy::U8) => this.tcx.types.u16,
+            ty::Uint(UintTy::U16) => this.tcx.types.u32,
+            ty::Uint(UintTy::U32) => this.tcx.types.u64,
+            ty::Uint(UintTy::U64) => this.tcx.types.u128,
+            // Signed
+            ty::Int(IntTy::I8) => this.tcx.types.i16,
+            ty::Int(IntTy::I16) => this.tcx.types.i32,
+            ty::Int(IntTy::I32) => this.tcx.types.i64,
+            ty::Int(IntTy::I64) => this.tcx.types.i128,
+            _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
+        }
+    }
 }
 
 impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
@@ -1151,3 +1169,20 @@ pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec<CrateNum> {
 pub fn target_os_is_unix(target_os: &str) -> bool {
     matches!(target_os, "linux" | "macos" | "freebsd" | "android")
 }
+
+pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Provenance> {
+    // SIMD uses all-1 as pattern for "true". In two's complement,
+    // -1 has all its bits set to one and `from_int` will truncate or
+    // sign-extend it to `size` as required.
+    let val = if b { -1 } else { 0 };
+    Scalar::from_int(val, size)
+}
+
+pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<'_, bool> {
+    let val = elem.to_scalar().to_int(elem.layout.size)?;
+    Ok(match val {
+        0 => false,
+        -1 => true,
+        _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
+    })
+}
diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs
index bfec4833ac9..ee2edd462d1 100644
--- a/src/tools/miri/src/shims/backtrace.rs
+++ b/src/tools/miri/src/shims/backtrace.rs
@@ -88,10 +88,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     this.write_pointer(ptr, &place)?;
                 }
 
-                this.write_immediate(
-                    Immediate::new_slice(Scalar::from_maybe_pointer(alloc.ptr(), this), len, this),
-                    dest,
-                )?;
+                this.write_immediate(Immediate::new_slice(alloc.ptr(), len, this), dest)?;
             }
             // storage for pointers is allocated by the caller
             1 => {
diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs
index dd8c4a4f6ec..626ead378e7 100644
--- a/src/tools/miri/src/shims/intrinsics/simd.rs
+++ b/src/tools/miri/src/shims/intrinsics/simd.rs
@@ -1,10 +1,10 @@
 use rustc_apfloat::{Float, Round};
 use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
 use rustc_middle::{mir, ty, ty::FloatTy};
-use rustc_target::abi::{Endian, HasDataLayout, Size};
+use rustc_target::abi::{Endian, HasDataLayout};
 
 use crate::*;
-use helpers::check_arg_count;
+use helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool};
 
 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
@@ -612,21 +612,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     }
 }
 
-fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Provenance> {
-    // SIMD uses all-1 as pattern for "true"
-    let val = if b { -1 } else { 0 };
-    Scalar::from_int(val, size)
-}
-
-fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<'_, bool> {
-    let val = elem.to_scalar().to_int(elem.layout.size)?;
-    Ok(match val {
-        0 => false,
-        -1 => true,
-        _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
-    })
-}
-
 fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 {
     assert!(idx < vec_len);
     match endianness {
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index 62f5eb1baf7..ccc729aae1a 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -1,4 +1,8 @@
-use crate::InterpResult;
+use rustc_middle::mir;
+use rustc_target::abi::Size;
+
+use crate::*;
+use helpers::bool_to_simd_element;
 
 pub(super) mod sse;
 pub(super) mod sse2;
@@ -43,3 +47,155 @@ impl FloatCmpOp {
         }
     }
 }
+
+#[derive(Copy, Clone)]
+enum FloatBinOp {
+    /// Arithmetic operation
+    Arith(mir::BinOp),
+    /// Comparison
+    Cmp(FloatCmpOp),
+    /// Minimum value (with SSE semantics)
+    ///
+    /// <https://www.felixcloutier.com/x86/minss>
+    /// <https://www.felixcloutier.com/x86/minps>
+    /// <https://www.felixcloutier.com/x86/minsd>
+    /// <https://www.felixcloutier.com/x86/minpd>
+    Min,
+    /// Maximum value (with SSE semantics)
+    ///
+    /// <https://www.felixcloutier.com/x86/maxss>
+    /// <https://www.felixcloutier.com/x86/maxps>
+    /// <https://www.felixcloutier.com/x86/maxsd>
+    /// <https://www.felixcloutier.com/x86/maxpd>
+    Max,
+}
+
+/// Performs `which` scalar operation on `left` and `right` and returns
+/// the result.
+fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
+    this: &crate::MiriInterpCx<'_, 'tcx>,
+    which: FloatBinOp,
+    left: &ImmTy<'tcx, Provenance>,
+    right: &ImmTy<'tcx, Provenance>,
+) -> InterpResult<'tcx, Scalar<Provenance>> {
+    match which {
+        FloatBinOp::Arith(which) => {
+            let (res, _overflow, _ty) = this.overflowing_binary_op(which, left, right)?;
+            Ok(res)
+        }
+        FloatBinOp::Cmp(which) => {
+            let left = left.to_scalar().to_float::<F>()?;
+            let right = right.to_scalar().to_float::<F>()?;
+            // FIXME: Make sure that these operations match the semantics
+            // of cmpps/cmpss/cmppd/cmpsd
+            let res = match which {
+                FloatCmpOp::Eq => left == right,
+                FloatCmpOp::Lt => left < right,
+                FloatCmpOp::Le => left <= right,
+                FloatCmpOp::Unord => left.is_nan() || right.is_nan(),
+                FloatCmpOp::Neq => left != right,
+                FloatCmpOp::Nlt => !(left < right),
+                FloatCmpOp::Nle => !(left <= right),
+                FloatCmpOp::Ord => !left.is_nan() && !right.is_nan(),
+            };
+            Ok(bool_to_simd_element(res, Size::from_bits(F::BITS)))
+        }
+        FloatBinOp::Min => {
+            let left_scalar = left.to_scalar();
+            let left = left_scalar.to_float::<F>()?;
+            let right_scalar = right.to_scalar();
+            let right = right_scalar.to_float::<F>()?;
+            // SSE semantics to handle zero and NaN. Note that `x == F::ZERO`
+            // is true when `x` is either +0 or -0.
+            if (left == F::ZERO && right == F::ZERO)
+                || left.is_nan()
+                || right.is_nan()
+                || left >= right
+            {
+                Ok(right_scalar)
+            } else {
+                Ok(left_scalar)
+            }
+        }
+        FloatBinOp::Max => {
+            let left_scalar = left.to_scalar();
+            let left = left_scalar.to_float::<F>()?;
+            let right_scalar = right.to_scalar();
+            let right = right_scalar.to_float::<F>()?;
+            // SSE semantics to handle zero and NaN. Note that `x == F::ZERO`
+            // is true when `x` is either +0 or -0.
+            if (left == F::ZERO && right == F::ZERO)
+                || left.is_nan()
+                || right.is_nan()
+                || left <= right
+            {
+                Ok(right_scalar)
+            } else {
+                Ok(left_scalar)
+            }
+        }
+    }
+}
+
+/// Performs `which` operation on the first component of `left` and `right`
+/// and copies the other components from `left`. The result is stored in `dest`.
+fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
+    this: &mut crate::MiriInterpCx<'_, 'tcx>,
+    which: FloatBinOp,
+    left: &OpTy<'tcx, Provenance>,
+    right: &OpTy<'tcx, Provenance>,
+    dest: &PlaceTy<'tcx, Provenance>,
+) -> InterpResult<'tcx, ()> {
+    let (left, left_len) = this.operand_to_simd(left)?;
+    let (right, right_len) = this.operand_to_simd(right)?;
+    let (dest, dest_len) = this.place_to_simd(dest)?;
+
+    assert_eq!(dest_len, left_len);
+    assert_eq!(dest_len, right_len);
+
+    let res0 = bin_op_float::<F>(
+        this,
+        which,
+        &this.read_immediate(&this.project_index(&left, 0)?)?,
+        &this.read_immediate(&this.project_index(&right, 0)?)?,
+    )?;
+    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+
+    for i in 1..dest_len {
+        this.copy_op(
+            &this.project_index(&left, i)?,
+            &this.project_index(&dest, i)?,
+            /*allow_transmute*/ false,
+        )?;
+    }
+
+    Ok(())
+}
+
+/// Performs `which` operation on each component of `left` and
+/// `right`, storing the result is stored in `dest`.
+fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
+    this: &mut crate::MiriInterpCx<'_, 'tcx>,
+    which: FloatBinOp,
+    left: &OpTy<'tcx, Provenance>,
+    right: &OpTy<'tcx, Provenance>,
+    dest: &PlaceTy<'tcx, Provenance>,
+) -> InterpResult<'tcx, ()> {
+    let (left, left_len) = this.operand_to_simd(left)?;
+    let (right, right_len) = this.operand_to_simd(right)?;
+    let (dest, dest_len) = this.place_to_simd(dest)?;
+
+    assert_eq!(dest_len, left_len);
+    assert_eq!(dest_len, right_len);
+
+    for i in 0..dest_len {
+        let left = this.read_immediate(&this.project_index(&left, i)?)?;
+        let right = this.read_immediate(&this.project_index(&right, i)?)?;
+        let dest = this.project_index(&dest, i)?;
+
+        let res = bin_op_float::<F>(this, which, &left, &right)?;
+        this.write_scalar(res, &dest)?;
+    }
+
+    Ok(())
+}
diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs
index ff4bd369706..30ad088206a 100644
--- a/src/tools/miri/src/shims/x86/sse.rs
+++ b/src/tools/miri/src/shims/x86/sse.rs
@@ -5,7 +5,7 @@ use rustc_target::spec::abi::Abi;
 
 use rand::Rng as _;
 
-use super::FloatCmpOp;
+use super::{bin_op_simd_float_all, bin_op_simd_float_first, FloatBinOp, FloatCmpOp};
 use crate::*;
 use shims::foreign_items::EmulateByNameResult;
 
@@ -45,7 +45,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     _ => unreachable!(),
                 };
 
-                bin_op_ss(this, which, left, right, dest)?;
+                bin_op_simd_float_first::<Single>(this, which, left, right, dest)?;
             }
             // Used to implement _mm_min_ps and _mm_max_ps functions.
             // Note that the semantics are a bit different from Rust simd_min
@@ -62,7 +62,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     _ => unreachable!(),
                 };
 
-                bin_op_ps(this, which, left, right, dest)?;
+                bin_op_simd_float_all::<Single>(this, which, left, right, dest)?;
             }
             // Used to implement _mm_{sqrt,rcp,rsqrt}_ss functions.
             // Performs the operations on the first component of `op` and
@@ -106,7 +106,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     "llvm.x86.sse.cmp.ss",
                 )?);
 
-                bin_op_ss(this, which, left, right, dest)?;
+                bin_op_simd_float_first::<Single>(this, which, left, right, dest)?;
             }
             // Used to implement the _mm_cmp_ps function.
             // Performs a comparison operation on each component of `left`
@@ -121,7 +121,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     "llvm.x86.sse.cmp.ps",
                 )?);
 
-                bin_op_ps(this, which, left, right, dest)?;
+                bin_op_simd_float_all::<Single>(this, which, left, right, dest)?;
             }
             // Used to implement _mm_{,u}comi{eq,lt,le,gt,ge,neq}_ss functions.
             // Compares the first component of `left` and `right` and returns
@@ -154,9 +154,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 };
                 this.write_scalar(Scalar::from_i32(i32::from(res)), dest)?;
             }
-            // Use to implement _mm_cvtss_si32 and _mm_cvttss_si32.
-            // Converts the first component of `op` from f32 to i32.
-            "cvtss2si" | "cvttss2si" => {
+            // Use to implement the _mm_cvtss_si32, _mm_cvttss_si32,
+            // _mm_cvtss_si64 and _mm_cvttss_si64 functions.
+            // Converts the first component of `op` from f32 to i32/i64.
+            "cvtss2si" | "cvttss2si" | "cvtss2si64" | "cvttss2si64" => {
                 let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let (op, _) = this.operand_to_simd(op)?;
 
@@ -165,77 +166,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let rnd = match unprefixed_name {
                     // "current SSE rounding mode", assume nearest
                     // https://www.felixcloutier.com/x86/cvtss2si
-                    "cvtss2si" => rustc_apfloat::Round::NearestTiesToEven,
+                    "cvtss2si" | "cvtss2si64" => rustc_apfloat::Round::NearestTiesToEven,
                     // always truncate
                     // https://www.felixcloutier.com/x86/cvttss2si
-                    "cvttss2si" => rustc_apfloat::Round::TowardZero,
+                    "cvttss2si" | "cvttss2si64" => rustc_apfloat::Round::TowardZero,
                     _ => unreachable!(),
                 };
 
                 let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
                     // Fallback to minimum acording to SSE semantics.
-                    Scalar::from_i32(i32::MIN)
+                    Scalar::from_int(dest.layout.size.signed_int_min(), dest.layout.size)
                 });
 
                 this.write_scalar(res, dest)?;
             }
-            // Use to implement _mm_cvtss_si64 and _mm_cvttss_si64.
-            // Converts the first component of `op` from f32 to i64.
-            "cvtss2si64" | "cvttss2si64" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                let (op, _) = this.operand_to_simd(op)?;
-
-                let op = this.read_scalar(&this.project_index(&op, 0)?)?.to_f32()?;
-
-                let rnd = match unprefixed_name {
-                    // "current SSE rounding mode", assume nearest
-                    // https://www.felixcloutier.com/x86/cvtss2si
-                    "cvtss2si64" => rustc_apfloat::Round::NearestTiesToEven,
-                    // always truncate
-                    // https://www.felixcloutier.com/x86/cvttss2si
-                    "cvttss2si64" => rustc_apfloat::Round::TowardZero,
-                    _ => unreachable!(),
-                };
-
-                let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
-                    // Fallback to minimum acording to SSE semantics.
-                    Scalar::from_i64(i64::MIN)
-                });
-
-                this.write_scalar(res, dest)?;
-            }
-            // Used to implement the _mm_cvtsi32_ss function.
-            // Converts `right` from i32 to f32. Returns a SIMD vector with
-            // the result in the first component and the remaining components
-            // are copied from `left`.
-            // https://www.felixcloutier.com/x86/cvtsi2ss
-            "cvtsi2ss" => {
-                let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-
-                let (left, left_len) = this.operand_to_simd(left)?;
-                let (dest, dest_len) = this.place_to_simd(dest)?;
-
-                assert_eq!(dest_len, left_len);
-
-                let right = this.read_scalar(right)?.to_i32()?;
-
-                let res0 = Scalar::from_f32(Single::from_i128(right.into()).value);
-                this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
-
-                for i in 1..dest_len {
-                    let left = this.read_immediate(&this.project_index(&left, i)?)?;
-                    let dest = this.project_index(&dest, i)?;
-
-                    this.write_immediate(*left, &dest)?;
-                }
-            }
-            // Used to implement the _mm_cvtsi64_ss function.
-            // Converts `right` from i64 to f32. Returns a SIMD vector with
+            // Used to implement the _mm_cvtsi32_ss and _mm_cvtsi64_ss functions.
+            // Converts `right` from i32/i64 to f32. Returns a SIMD vector with
             // the result in the first component and the remaining components
             // are copied from `left`.
             // https://www.felixcloutier.com/x86/cvtsi2ss
-            "cvtsi642ss" => {
+            "cvtsi2ss" | "cvtsi642ss" => {
                 let [left, right] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
 
@@ -244,16 +194,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
                 assert_eq!(dest_len, left_len);
 
-                let right = this.read_scalar(right)?.to_i64()?;
-
-                let res0 = Scalar::from_f32(Single::from_i128(right.into()).value);
-                this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+                let right = this.read_immediate(right)?;
+                let dest0 = this.project_index(&dest, 0)?;
+                let res0 = this.int_to_int_or_float(&right, dest0.layout.ty)?;
+                this.write_immediate(res0, &dest0)?;
 
                 for i in 1..dest_len {
-                    let left = this.read_immediate(&this.project_index(&left, i)?)?;
-                    let dest = this.project_index(&dest, i)?;
-
-                    this.write_immediate(*left, &dest)?;
+                    this.copy_op(
+                        &this.project_index(&left, i)?,
+                        &this.project_index(&dest, i)?,
+                        /*allow_transmute*/ false,
+                    )?;
                 }
             }
             // Used to implement the _mm_movemask_ps function.
@@ -282,148 +233,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 }
 
 #[derive(Copy, Clone)]
-enum FloatBinOp {
-    /// Arithmetic operation
-    Arith(mir::BinOp),
-    /// Comparison
-    Cmp(FloatCmpOp),
-    /// Minimum value (with SSE semantics)
-    ///
-    /// <https://www.felixcloutier.com/x86/minss>
-    /// <https://www.felixcloutier.com/x86/minps>
-    Min,
-    /// Maximum value (with SSE semantics)
-    ///
-    /// <https://www.felixcloutier.com/x86/maxss>
-    /// <https://www.felixcloutier.com/x86/maxps>
-    Max,
-}
-
-/// Performs `which` scalar operation on `left` and `right` and returns
-/// the result.
-fn bin_op_f32<'tcx>(
-    this: &crate::MiriInterpCx<'_, 'tcx>,
-    which: FloatBinOp,
-    left: &ImmTy<'tcx, Provenance>,
-    right: &ImmTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    match which {
-        FloatBinOp::Arith(which) => {
-            let (res, _, _) = this.overflowing_binary_op(which, left, right)?;
-            Ok(res)
-        }
-        FloatBinOp::Cmp(which) => {
-            let left = left.to_scalar().to_f32()?;
-            let right = right.to_scalar().to_f32()?;
-            // FIXME: Make sure that these operations match the semantics of cmpps
-            let res = match which {
-                FloatCmpOp::Eq => left == right,
-                FloatCmpOp::Lt => left < right,
-                FloatCmpOp::Le => left <= right,
-                FloatCmpOp::Unord => left.is_nan() || right.is_nan(),
-                FloatCmpOp::Neq => left != right,
-                FloatCmpOp::Nlt => !(left < right),
-                FloatCmpOp::Nle => !(left <= right),
-                FloatCmpOp::Ord => !left.is_nan() && !right.is_nan(),
-            };
-            Ok(Scalar::from_u32(if res { u32::MAX } else { 0 }))
-        }
-        FloatBinOp::Min => {
-            let left = left.to_scalar().to_f32()?;
-            let right = right.to_scalar().to_f32()?;
-            // SSE semantics to handle zero and NaN. Note that `x == Single::ZERO`
-            // is true when `x` is either +0 or -0.
-            if (left == Single::ZERO && right == Single::ZERO)
-                || left.is_nan()
-                || right.is_nan()
-                || left >= right
-            {
-                Ok(Scalar::from_f32(right))
-            } else {
-                Ok(Scalar::from_f32(left))
-            }
-        }
-        FloatBinOp::Max => {
-            let left = left.to_scalar().to_f32()?;
-            let right = right.to_scalar().to_f32()?;
-            // SSE semantics to handle zero and NaN. Note that `x == Single::ZERO`
-            // is true when `x` is either +0 or -0.
-            if (left == Single::ZERO && right == Single::ZERO)
-                || left.is_nan()
-                || right.is_nan()
-                || left <= right
-            {
-                Ok(Scalar::from_f32(right))
-            } else {
-                Ok(Scalar::from_f32(left))
-            }
-        }
-    }
-}
-
-/// Performs `which` operation on the first component of `left` and `right`
-/// and copies the other components from `left`. The result is stored in `dest`.
-fn bin_op_ss<'tcx>(
-    this: &mut crate::MiriInterpCx<'_, 'tcx>,
-    which: FloatBinOp,
-    left: &OpTy<'tcx, Provenance>,
-    right: &OpTy<'tcx, Provenance>,
-    dest: &PlaceTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.operand_to_simd(left)?;
-    let (right, right_len) = this.operand_to_simd(right)?;
-    let (dest, dest_len) = this.place_to_simd(dest)?;
-
-    assert_eq!(dest_len, left_len);
-    assert_eq!(dest_len, right_len);
-
-    let res0 = bin_op_f32(
-        this,
-        which,
-        &this.read_immediate(&this.project_index(&left, 0)?)?,
-        &this.read_immediate(&this.project_index(&right, 0)?)?,
-    )?;
-    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
-
-    for i in 1..dest_len {
-        let left = this.read_immediate(&this.project_index(&left, i)?)?;
-        let dest = this.project_index(&dest, i)?;
-
-        this.write_immediate(*left, &dest)?;
-    }
-
-    Ok(())
-}
-
-/// Performs `which` operation on each component of `left` and
-/// `right`, storing the result is stored in `dest`.
-fn bin_op_ps<'tcx>(
-    this: &mut crate::MiriInterpCx<'_, 'tcx>,
-    which: FloatBinOp,
-    left: &OpTy<'tcx, Provenance>,
-    right: &OpTy<'tcx, Provenance>,
-    dest: &PlaceTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.operand_to_simd(left)?;
-    let (right, right_len) = this.operand_to_simd(right)?;
-    let (dest, dest_len) = this.place_to_simd(dest)?;
-
-    assert_eq!(dest_len, left_len);
-    assert_eq!(dest_len, right_len);
-
-    for i in 0..dest_len {
-        let left = this.read_immediate(&this.project_index(&left, i)?)?;
-        let right = this.read_immediate(&this.project_index(&right, i)?)?;
-        let dest = this.project_index(&dest, i)?;
-
-        let res = bin_op_f32(this, which, &left, &right)?;
-        this.write_scalar(res, &dest)?;
-    }
-
-    Ok(())
-}
-
-#[derive(Copy, Clone)]
 enum FloatUnaryOp {
     /// sqrt(x)
     ///
@@ -510,10 +319,11 @@ fn unary_op_ss<'tcx>(
     this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
 
     for i in 1..dest_len {
-        let op = this.read_immediate(&this.project_index(&op, i)?)?;
-        let dest = this.project_index(&dest, i)?;
-
-        this.write_immediate(*op, &dest)?;
+        this.copy_op(
+            &this.project_index(&op, i)?,
+            &this.project_index(&dest, i)?,
+            /*allow_transmute*/ false,
+        )?;
     }
 
     Ok(())
diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs
index 5b42339e648..d3bfb53afd2 100644
--- a/src/tools/miri/src/shims/x86/sse2.rs
+++ b/src/tools/miri/src/shims/x86/sse2.rs
@@ -1,14 +1,14 @@
 use rustc_apfloat::{
     ieee::{Double, Single},
-    Float as _, FloatConvert as _,
+    Float as _,
 };
+use rustc_middle::mir;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_middle::ty::Ty;
 use rustc_span::Symbol;
-use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi;
 
-use super::FloatCmpOp;
+use super::{bin_op_simd_float_all, bin_op_simd_float_first, FloatBinOp, FloatCmpOp};
 use crate::*;
 use shims::foreign_items::EmulateByNameResult;
 
@@ -37,9 +37,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // Intrinsincs sufixed with "epiX" or "epuX" operate with X-bit signed or unsigned
         // vectors.
         match unprefixed_name {
-            // Used to implement the _mm_avg_epu8 function.
-            // Averages packed unsigned 8-bit integers in `left` and `right`.
-            "pavg.b" => {
+            // Used to implement the _mm_avg_epu8 and _mm_avg_epu16 functions.
+            // Averages packed unsigned 8/16-bit integers in `left` and `right`.
+            "pavg.b" | "pavg.w" => {
                 let [left, right] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
 
@@ -51,50 +51,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 assert_eq!(dest_len, right_len);
 
                 for i in 0..dest_len {
-                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u8()?;
-                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?;
+                    let left = this.read_immediate(&this.project_index(&left, i)?)?;
+                    let right = this.read_immediate(&this.project_index(&right, i)?)?;
                     let dest = this.project_index(&dest, i)?;
 
-                    // Values are expanded from u8 to u16, so adds cannot overflow.
-                    let res = u16::from(left)
-                        .checked_add(u16::from(right))
-                        .unwrap()
-                        .checked_add(1)
-                        .unwrap()
-                        / 2;
-                    this.write_scalar(Scalar::from_u8(res.try_into().unwrap()), &dest)?;
-                }
-            }
-            // Used to implement the _mm_avg_epu16 function.
-            // Averages packed unsigned 16-bit integers in `left` and `right`.
-            "pavg.w" => {
-                let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-
-                let (left, left_len) = this.operand_to_simd(left)?;
-                let (right, right_len) = this.operand_to_simd(right)?;
-                let (dest, dest_len) = this.place_to_simd(dest)?;
-
-                assert_eq!(dest_len, left_len);
-                assert_eq!(dest_len, right_len);
+                    // Widen the operands to avoid overflow
+                    let twice_wide_ty = this.get_twice_wide_int_ty(left.layout.ty);
+                    let twice_wide_layout = this.layout_of(twice_wide_ty)?;
+                    let left = this.int_to_int_or_float(&left, twice_wide_ty)?;
+                    let right = this.int_to_int_or_float(&right, twice_wide_ty)?;
+
+                    // Calculate left + right + 1
+                    let (added, _overflow, _ty) = this.overflowing_binary_op(
+                        mir::BinOp::Add,
+                        &ImmTy::from_immediate(left, twice_wide_layout),
+                        &ImmTy::from_immediate(right, twice_wide_layout),
+                    )?;
+                    let (added, _overflow, _ty) = this.overflowing_binary_op(
+                        mir::BinOp::Add,
+                        &ImmTy::from_scalar(added, twice_wide_layout),
+                        &ImmTy::from_uint(1u32, twice_wide_layout),
+                    )?;
 
-                for i in 0..dest_len {
-                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?;
-                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u16()?;
-                    let dest = this.project_index(&dest, i)?;
+                    // Calculate (left + right + 1) / 2
+                    let (divided, _overflow, _ty) = this.overflowing_binary_op(
+                        mir::BinOp::Div,
+                        &ImmTy::from_scalar(added, twice_wide_layout),
+                        &ImmTy::from_uint(2u32, twice_wide_layout),
+                    )?;
 
-                    // Values are expanded from u16 to u32, so adds cannot overflow.
-                    let res = u32::from(left)
-                        .checked_add(u32::from(right))
-                        .unwrap()
-                        .checked_add(1)
-                        .unwrap()
-                        / 2;
-                    this.write_scalar(Scalar::from_u16(res.try_into().unwrap()), &dest)?;
+                    // Narrow back to the original type
+                    let res = this.int_to_int_or_float(
+                        &ImmTy::from_scalar(divided, twice_wide_layout),
+                        dest.layout.ty,
+                    )?;
+                    this.write_immediate(res, &dest)?;
                 }
             }
-            // Used to implement the _mm_mulhi_epi16 function.
-            "pmulh.w" => {
+            // Used to implement the _mm_mulhi_epi16 and _mm_mulhi_epu16 functions.
+            "pmulh.w" | "pmulhu.w" => {
                 let [left, right] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
 
@@ -106,35 +101,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 assert_eq!(dest_len, right_len);
 
                 for i in 0..dest_len {
-                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?;
-                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?;
+                    let left = this.read_immediate(&this.project_index(&left, i)?)?;
+                    let right = this.read_immediate(&this.project_index(&right, i)?)?;
                     let dest = this.project_index(&dest, i)?;
 
-                    // Values are expanded from i16 to i32, so multiplication cannot overflow.
-                    let res = i32::from(left).checked_mul(i32::from(right)).unwrap() >> 16;
-                    this.write_scalar(Scalar::from_int(res, Size::from_bits(16)), &dest)?;
-                }
-            }
-            // Used to implement the _mm_mulhi_epu16 function.
-            "pmulhu.w" => {
-                let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-
-                let (left, left_len) = this.operand_to_simd(left)?;
-                let (right, right_len) = this.operand_to_simd(right)?;
-                let (dest, dest_len) = this.place_to_simd(dest)?;
-
-                assert_eq!(dest_len, left_len);
-                assert_eq!(dest_len, right_len);
-
-                for i in 0..dest_len {
-                    let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?;
-                    let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u16()?;
-                    let dest = this.project_index(&dest, i)?;
+                    // Widen the operands to avoid overflow
+                    let twice_wide_ty = this.get_twice_wide_int_ty(left.layout.ty);
+                    let twice_wide_layout = this.layout_of(twice_wide_ty)?;
+                    let left = this.int_to_int_or_float(&left, twice_wide_ty)?;
+                    let right = this.int_to_int_or_float(&right, twice_wide_ty)?;
+
+                    // Multiply
+                    let (multiplied, _overflow, _ty) = this.overflowing_binary_op(
+                        mir::BinOp::Mul,
+                        &ImmTy::from_immediate(left, twice_wide_layout),
+                        &ImmTy::from_immediate(right, twice_wide_layout),
+                    )?;
+                    // Keep the high half
+                    let (high, _overflow, _ty) = this.overflowing_binary_op(
+                        mir::BinOp::Shr,
+                        &ImmTy::from_scalar(multiplied, twice_wide_layout),
+                        &ImmTy::from_uint(dest.layout.size.bits(), twice_wide_layout),
+                    )?;
 
-                    // Values are expanded from u16 to u32, so multiplication cannot overflow.
-                    let res = u32::from(left).checked_mul(u32::from(right)).unwrap() >> 16;
-                    this.write_scalar(Scalar::from_u16(res.try_into().unwrap()), &dest)?;
+                    // Narrow back to the original type
+                    let res = this.int_to_int_or_float(
+                        &ImmTy::from_scalar(high, twice_wide_layout),
+                        dest.layout.ty,
+                    )?;
+                    this.write_immediate(res, &dest)?;
                 }
             }
             // Used to implement the _mm_mul_epu32 function.
@@ -431,11 +426,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     let right_res =
                         i8::try_from(right).unwrap_or(if right < 0 { i8::MIN } else { i8::MAX });
 
-                    this.write_scalar(Scalar::from_int(left_res, Size::from_bits(8)), &left_dest)?;
-                    this.write_scalar(
-                        Scalar::from_int(right_res, Size::from_bits(8)),
-                        &right_dest,
-                    )?;
+                    this.write_scalar(Scalar::from_i8(left_res), &left_dest)?;
+                    this.write_scalar(Scalar::from_i8(right_res), &right_dest)?;
                 }
             }
             // Used to implement the _mm_packus_epi16 function.
@@ -469,7 +461,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 }
             }
             // Used to implement the _mm_packs_epi32 function.
-            // Converts two 16-bit integer vectors to a single 8-bit integer
+            // Converts two 32-bit integer vectors to a single 16-bit integer
             // vector with signed saturation.
             "packssdw.128" => {
                 let [left, right] =
@@ -495,11 +487,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     let right_res =
                         i16::try_from(right).unwrap_or(if right < 0 { i16::MIN } else { i16::MAX });
 
-                    this.write_scalar(Scalar::from_int(left_res, Size::from_bits(16)), &left_dest)?;
-                    this.write_scalar(
-                        Scalar::from_int(right_res, Size::from_bits(16)),
-                        &right_dest,
-                    )?;
+                    this.write_scalar(Scalar::from_i16(left_res), &left_dest)?;
+                    this.write_scalar(Scalar::from_i16(right_res), &right_dest)?;
                 }
             }
             // Used to implement _mm_min_sd and _mm_max_sd functions.
@@ -517,7 +506,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     _ => unreachable!(),
                 };
 
-                bin_op_sd(this, which, left, right, dest)?;
+                bin_op_simd_float_first::<Double>(this, which, left, right, dest)?;
             }
             // Used to implement _mm_min_pd and _mm_max_pd functions.
             // Note that the semantics are a bit different from Rust simd_min
@@ -534,7 +523,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     _ => unreachable!(),
                 };
 
-                bin_op_pd(this, which, left, right, dest)?;
+                bin_op_simd_float_all::<Double>(this, which, left, right, dest)?;
             }
             // Used to implement _mm_sqrt_sd functions.
             // Performs the operations on the first component of `op` and
@@ -593,7 +582,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     "llvm.x86.sse2.cmp.sd",
                 )?);
 
-                bin_op_sd(this, which, left, right, dest)?;
+                bin_op_simd_float_first::<Double>(this, which, left, right, dest)?;
             }
             // Used to implement the _mm_cmp*_pd functions.
             // Performs a comparison operation on each component of `left`
@@ -608,7 +597,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     "llvm.x86.sse2.cmp.pd",
                 )?);
 
-                bin_op_pd(this, which, left, right, dest)?;
+                bin_op_simd_float_all::<Double>(this, which, left, right, dest)?;
             }
             // Used to implement _mm_{,u}comi{eq,lt,le,gt,ge,neq}_sd functions.
             // Compares the first component of `left` and `right` and returns
@@ -641,52 +630,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 };
                 this.write_scalar(Scalar::from_i32(i32::from(res)), dest)?;
             }
-            // Used to implement the _mm_cvtpd_ps function.
-            // Converts packed f32 to packed f64.
-            "cvtpd2ps" => {
+            // Used to implement the _mm_cvtpd_ps and _mm_cvtps_pd functions.
+            // Converts packed f32/f64 to packed f64/f32.
+            "cvtpd2ps" | "cvtps2pd" => {
                 let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
 
                 let (op, op_len) = this.operand_to_simd(op)?;
                 let (dest, dest_len) = this.place_to_simd(dest)?;
 
-                // op is f64x2, dest is f32x4
-                assert_eq!(op_len, 2);
-                assert_eq!(dest_len, 4);
-
-                for i in 0..op_len {
-                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f64()?;
+                // For cvtpd2ps: op is f64x2, dest is f32x4
+                // For cvtps2pd: op is f32x4, dest is f64x2
+                // In either case, the two first values are converted
+                for i in 0..op_len.min(dest_len) {
+                    let op = this.read_immediate(&this.project_index(&op, i)?)?;
                     let dest = this.project_index(&dest, i)?;
 
-                    let res = op.convert(/*loses_info*/ &mut false).value;
-                    this.write_scalar(Scalar::from_f32(res), &dest)?;
+                    let res = this.float_to_float_or_int(&op, dest.layout.ty)?;
+                    this.write_immediate(res, &dest)?;
                 }
-                // Fill the remaining with zeros
+                // For f32 -> f64, ignore the remaining
+                // For f64 -> f32, fill the remaining with zeros
                 for i in op_len..dest_len {
                     let dest = this.project_index(&dest, i)?;
-                    this.write_scalar(Scalar::from_u32(0), &dest)?;
+                    this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
                 }
             }
-            // Used to implement the _mm_cvtps_pd function.
-            // Converts packed f64 to packed f32.
-            "cvtps2pd" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-
-                let (op, op_len) = this.operand_to_simd(op)?;
-                let (dest, dest_len) = this.place_to_simd(dest)?;
-
-                // op is f32x4, dest is f64x2
-                assert_eq!(op_len, 4);
-                assert_eq!(dest_len, 2);
-
-                for i in 0..dest_len {
-                    let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f32()?;
-                    let dest = this.project_index(&dest, i)?;
-
-                    let res = op.convert(/*loses_info*/ &mut false).value;
-                    this.write_scalar(Scalar::from_f64(res), &dest)?;
-                }
-                // the two remaining f32 are ignored
-            }
             // Used to implement the _mm_cvtpd_epi32 and _mm_cvttpd_epi32 functions.
             // Converts packed f64 to packed i32.
             "cvtpd2dq" | "cvttpd2dq" => {
@@ -726,34 +694,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     this.write_scalar(Scalar::from_i32(0), &dest)?;
                 }
             }
-            // Use to implement the _mm_cvtsd_si32 and _mm_cvttsd_si32 functions.
-            // Converts the first component of `op` from f64 to i32.
-            "cvtsd2si" | "cvttsd2si" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                let (op, _) = this.operand_to_simd(op)?;
-
-                let op = this.read_scalar(&this.project_index(&op, 0)?)?.to_f64()?;
-
-                let rnd = match unprefixed_name {
-                    // "current SSE rounding mode", assume nearest
-                    // https://www.felixcloutier.com/x86/cvtsd2si
-                    "cvtsd2si" => rustc_apfloat::Round::NearestTiesToEven,
-                    // always truncate
-                    // https://www.felixcloutier.com/x86/cvttsd2si
-                    "cvttsd2si" => rustc_apfloat::Round::TowardZero,
-                    _ => unreachable!(),
-                };
-
-                let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
-                    // Fallback to minimum acording to SSE semantics.
-                    Scalar::from_i32(i32::MIN)
-                });
-
-                this.write_scalar(res, dest)?;
-            }
-            // Use to implement the _mm_cvtsd_si64 and _mm_cvttsd_si64 functions.
-            // Converts the first component of `op` from f64 to i64.
-            "cvtsd2si64" | "cvttsd2si64" => {
+            // Use to implement the _mm_cvtsd_si32, _mm_cvttsd_si32,
+            // _mm_cvtsd_si64 and _mm_cvttsd_si64 functions.
+            // Converts the first component of `op` from f64 to i32/i64.
+            "cvtsd2si" | "cvttsd2si" | "cvtsd2si64" | "cvttsd2si64" => {
                 let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let (op, _) = this.operand_to_simd(op)?;
 
@@ -762,16 +706,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let rnd = match unprefixed_name {
                     // "current SSE rounding mode", assume nearest
                     // https://www.felixcloutier.com/x86/cvtsd2si
-                    "cvtsd2si64" => rustc_apfloat::Round::NearestTiesToEven,
+                    "cvtsd2si" | "cvtsd2si64" => rustc_apfloat::Round::NearestTiesToEven,
                     // always truncate
                     // https://www.felixcloutier.com/x86/cvttsd2si
-                    "cvttsd2si64" => rustc_apfloat::Round::TowardZero,
+                    "cvttsd2si" | "cvttsd2si64" => rustc_apfloat::Round::TowardZero,
                     _ => unreachable!(),
                 };
 
                 let res = this.float_to_int_checked(op, dest.layout.ty, rnd).unwrap_or_else(|| {
                     // Fallback to minimum acording to SSE semantics.
-                    Scalar::from_i64(i64::MIN)
+                    Scalar::from_int(dest.layout.size.signed_int_min(), dest.layout.size)
                 });
 
                 this.write_scalar(res, dest)?;
@@ -844,139 +788,3 @@ fn extract_first_u64<'tcx>(
     // Get the first u64 from the array
     this.read_scalar(&this.project_index(&op, 0)?)?.to_u64()
 }
-
-#[derive(Copy, Clone)]
-enum FloatBinOp {
-    /// Comparison
-    Cmp(FloatCmpOp),
-    /// Minimum value (with SSE semantics)
-    ///
-    /// <https://www.felixcloutier.com/x86/minsd>
-    /// <https://www.felixcloutier.com/x86/minpd>
-    Min,
-    /// Maximum value (with SSE semantics)
-    ///
-    /// <https://www.felixcloutier.com/x86/maxsd>
-    /// <https://www.felixcloutier.com/x86/maxpd>
-    Max,
-}
-
-/// Performs `which` scalar operation on `left` and `right` and returns
-/// the result.
-// FIXME make this generic over apfloat type to reduce code duplicaton with bin_op_f32
-fn bin_op_f64<'tcx>(
-    which: FloatBinOp,
-    left: &ImmTy<'tcx, Provenance>,
-    right: &ImmTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, Scalar<Provenance>> {
-    match which {
-        FloatBinOp::Cmp(which) => {
-            let left = left.to_scalar().to_f64()?;
-            let right = right.to_scalar().to_f64()?;
-            // FIXME: Make sure that these operations match the semantics of cmppd
-            let res = match which {
-                FloatCmpOp::Eq => left == right,
-                FloatCmpOp::Lt => left < right,
-                FloatCmpOp::Le => left <= right,
-                FloatCmpOp::Unord => left.is_nan() || right.is_nan(),
-                FloatCmpOp::Neq => left != right,
-                FloatCmpOp::Nlt => !(left < right),
-                FloatCmpOp::Nle => !(left <= right),
-                FloatCmpOp::Ord => !left.is_nan() && !right.is_nan(),
-            };
-            Ok(Scalar::from_u64(if res { u64::MAX } else { 0 }))
-        }
-        FloatBinOp::Min => {
-            let left = left.to_scalar().to_f64()?;
-            let right = right.to_scalar().to_f64()?;
-            // SSE semantics to handle zero and NaN. Note that `x == Single::ZERO`
-            // is true when `x` is either +0 or -0.
-            if (left == Double::ZERO && right == Double::ZERO)
-                || left.is_nan()
-                || right.is_nan()
-                || left >= right
-            {
-                Ok(Scalar::from_f64(right))
-            } else {
-                Ok(Scalar::from_f64(left))
-            }
-        }
-        FloatBinOp::Max => {
-            let left = left.to_scalar().to_f64()?;
-            let right = right.to_scalar().to_f64()?;
-            // SSE semantics to handle zero and NaN. Note that `x == Single::ZERO`
-            // is true when `x` is either +0 or -0.
-            if (left == Double::ZERO && right == Double::ZERO)
-                || left.is_nan()
-                || right.is_nan()
-                || left <= right
-            {
-                Ok(Scalar::from_f64(right))
-            } else {
-                Ok(Scalar::from_f64(left))
-            }
-        }
-    }
-}
-
-/// Performs `which` operation on the first component of `left` and `right`
-/// and copies the other components from `left`. The result is stored in `dest`.
-fn bin_op_sd<'tcx>(
-    this: &mut crate::MiriInterpCx<'_, 'tcx>,
-    which: FloatBinOp,
-    left: &OpTy<'tcx, Provenance>,
-    right: &OpTy<'tcx, Provenance>,
-    dest: &PlaceTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.operand_to_simd(left)?;
-    let (right, right_len) = this.operand_to_simd(right)?;
-    let (dest, dest_len) = this.place_to_simd(dest)?;
-
-    assert_eq!(dest_len, left_len);
-    assert_eq!(dest_len, right_len);
-
-    let res0 = bin_op_f64(
-        which,
-        &this.read_immediate(&this.project_index(&left, 0)?)?,
-        &this.read_immediate(&this.project_index(&right, 0)?)?,
-    )?;
-    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
-
-    for i in 1..dest_len {
-        this.copy_op(
-            &this.project_index(&left, i)?,
-            &this.project_index(&dest, i)?,
-            /*allow_transmute*/ false,
-        )?;
-    }
-
-    Ok(())
-}
-
-/// Performs `which` operation on each component of `left` and
-/// `right`, storing the result is stored in `dest`.
-fn bin_op_pd<'tcx>(
-    this: &mut crate::MiriInterpCx<'_, 'tcx>,
-    which: FloatBinOp,
-    left: &OpTy<'tcx, Provenance>,
-    right: &OpTy<'tcx, Provenance>,
-    dest: &PlaceTy<'tcx, Provenance>,
-) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.operand_to_simd(left)?;
-    let (right, right_len) = this.operand_to_simd(right)?;
-    let (dest, dest_len) = this.place_to_simd(dest)?;
-
-    assert_eq!(dest_len, left_len);
-    assert_eq!(dest_len, right_len);
-
-    for i in 0..dest_len {
-        let left = this.read_immediate(&this.project_index(&left, i)?)?;
-        let right = this.read_immediate(&this.project_index(&right, i)?)?;
-        let dest = this.project_index(&dest, i)?;
-
-        let res = bin_op_f64(which, &left, &right)?;
-        this.write_scalar(res, &dest)?;
-    }
-
-    Ok(())
-}
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 416517d15f5..c7e6dd3615e 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -23,7 +23,9 @@ function contentToDiffLine(key, value) {
 }
 
 function shouldIgnoreField(fieldName) {
-    return fieldName === "query" || fieldName === "correction";
+    return fieldName === "query" || fieldName === "correction" ||
+        fieldName === "proposeCorrectionFrom" ||
+        fieldName === "proposeCorrectionTo";
 }
 
 // This function is only called when no matching result was found and therefore will only display
diff --git a/src/tools/rustdoc-themes/main.rs b/src/tools/rustdoc-themes/main.rs
index cc13df1f5ba..1eba83a8057 100644
--- a/src/tools/rustdoc-themes/main.rs
+++ b/src/tools/rustdoc-themes/main.rs
@@ -1,5 +1,5 @@
 use std::env::args;
-use std::fs::File;
+use std::fs::{create_dir_all, File};
 use std::io::{BufRead, BufReader, BufWriter, Write};
 use std::path::Path;
 use std::process::{exit, Command};
@@ -14,6 +14,7 @@ fn get_themes<P: AsRef<Path>>(style_path: P) -> Vec<String> {
         std::time::SystemTime::UNIX_EPOCH.elapsed().expect("time is after UNIX epoch").as_millis();
 
     let mut in_theme = None;
+    create_dir_all("build/tmp").expect("failed to create temporary test directory");
     for line in BufReader::new(File::open(style_path).expect("read rustdoc.css failed")).lines() {
         let line = line.expect("read line from rustdoc.css failed");
         let line = line.trim();
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 410852b6a31..843ffe2c4c3 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -120,7 +120,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "annotate-snippets",
     "ar_archive_writer",
     "arrayvec",
-    "atty",
     "autocfg",
     "bitflags",
     "block-buffer",
@@ -181,6 +180,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "intl-memoizer",
     "intl_pluralrules",
     "io-lifetimes",
+    "is-terminal",
     "itertools",
     "itoa",
     "jobserver",
diff --git a/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs b/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs
index a70ef7751b6..e9dd04e1927 100644
--- a/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs
+++ b/tests/codegen/sanitizer/address-sanitizer-globals-tracking.rs
@@ -19,8 +19,9 @@
 // only-linux
 //
 // revisions:ASAN ASAN-FAT-LTO
-//[ASAN]          compile-flags: -Zsanitizer=address
-//[ASAN-FAT-LTO]  compile-flags: -Zsanitizer=address -Cprefer-dynamic=false -Clto=fat
+//                compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static
+//[ASAN]          compile-flags:
+//[ASAN-FAT-LTO]  compile-flags: -Cprefer-dynamic=false -Clto=fat
 
 #![crate_type="staticlib"]
 
diff --git a/tests/codegen/sanitizer/kcfi-add-kcfi-flag.rs b/tests/codegen/sanitizer/kcfi-add-kcfi-flag.rs
index c2eb852aec3..6d466b93c40 100644
--- a/tests/codegen/sanitizer/kcfi-add-kcfi-flag.rs
+++ b/tests/codegen/sanitizer/kcfi-add-kcfi-flag.rs
@@ -1,9 +1,20 @@
 // Verifies that "kcfi" module flag is added.
 //
-// needs-sanitizer-kcfi
+// revisions: aarch64 x86_64
+// [aarch64] compile-flags: --target aarch64-unknown-none
+// [aarch64] needs-llvm-components: aarch64
+// [x86_64] compile-flags: --target x86_64-unknown-none
+// [x86_64] needs-llvm-components: x86
 // compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi
 
+#![feature(no_core, lang_items)]
 #![crate_type="lib"]
+#![no_core]
+
+#[lang="sized"]
+trait Sized { }
+#[lang="copy"]
+trait Copy { }
 
 pub fn foo() {
 }
diff --git a/tests/codegen/sanitizer/memory-track-origins.rs b/tests/codegen/sanitizer/memory-track-origins.rs
index 4bd50508d15..e15a3b2274e 100644
--- a/tests/codegen/sanitizer/memory-track-origins.rs
+++ b/tests/codegen/sanitizer/memory-track-origins.rs
@@ -4,11 +4,12 @@
 // needs-sanitizer-memory
 // revisions:MSAN-0 MSAN-1 MSAN-2 MSAN-1-LTO MSAN-2-LTO
 //
-//[MSAN-0] compile-flags: -Zsanitizer=memory
-//[MSAN-1] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins=1
-//[MSAN-2] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins
-//[MSAN-1-LTO] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins=1 -C lto=fat
-//[MSAN-2-LTO] compile-flags: -Zsanitizer=memory -Zsanitizer-memory-track-origins -C lto=fat
+// compile-flags: -Zsanitizer=memory -Ctarget-feature=-crt-static
+//[MSAN-0] compile-flags:
+//[MSAN-1] compile-flags: -Zsanitizer-memory-track-origins=1
+//[MSAN-2] compile-flags: -Zsanitizer-memory-track-origins
+//[MSAN-1-LTO] compile-flags: -Zsanitizer-memory-track-origins=1 -C lto=fat
+//[MSAN-2-LTO] compile-flags: -Zsanitizer-memory-track-origins -C lto=fat
 
 #![crate_type="lib"]
 
diff --git a/tests/codegen/sanitizer/no-sanitize-inlining.rs b/tests/codegen/sanitizer/no-sanitize-inlining.rs
index f4af60baefe..e371b19eb36 100644
--- a/tests/codegen/sanitizer/no-sanitize-inlining.rs
+++ b/tests/codegen/sanitizer/no-sanitize-inlining.rs
@@ -4,8 +4,9 @@
 // needs-sanitizer-address
 // needs-sanitizer-leak
 // revisions: ASAN LSAN
-//[ASAN] compile-flags: -Zsanitizer=address -C opt-level=3 -Z mir-opt-level=4
-//[LSAN] compile-flags: -Zsanitizer=leak    -C opt-level=3 -Z mir-opt-level=4
+//       compile-flags: -Copt-level=3 -Zmir-opt-level=4 -Ctarget-feature=-crt-static
+//[ASAN] compile-flags: -Zsanitizer=address
+//[LSAN] compile-flags: -Zsanitizer=leak
 
 #![crate_type="lib"]
 #![feature(no_sanitize)]
diff --git a/tests/codegen/sanitizer/no-sanitize.rs b/tests/codegen/sanitizer/no-sanitize.rs
index 783b568e279..d0b69243453 100644
--- a/tests/codegen/sanitizer/no-sanitize.rs
+++ b/tests/codegen/sanitizer/no-sanitize.rs
@@ -2,7 +2,7 @@
 // selectively disable sanitizer instrumentation.
 //
 // needs-sanitizer-address
-// compile-flags: -Zsanitizer=address
+// compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static
 
 #![crate_type="lib"]
 #![feature(no_sanitize)]
diff --git a/tests/codegen/sanitizer/sanitizer-recover.rs b/tests/codegen/sanitizer/sanitizer-recover.rs
index 7b00fcf8e1b..59b1fdd6494 100644
--- a/tests/codegen/sanitizer/sanitizer-recover.rs
+++ b/tests/codegen/sanitizer/sanitizer-recover.rs
@@ -6,6 +6,7 @@
 // revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER MSAN-RECOVER-LTO
 // no-prefer-dynamic
 //
+//                   compile-flags: -Ctarget-feature=-crt-static
 //[ASAN]             compile-flags: -Zsanitizer=address -Copt-level=0
 //[ASAN-RECOVER]     compile-flags: -Zsanitizer=address -Zsanitizer-recover=address -Copt-level=0
 //[MSAN]             compile-flags: -Zsanitizer=memory
diff --git a/tests/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot b/tests/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot
deleted file mode 100644
index 3b90aaeae42..00000000000
--- a/tests/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot
+++ /dev/null
@@ -1,6 +0,0 @@
-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 align="left"/>    19:5-19:9: @0[0]: Coverage::Counter(0) 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/tests/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot b/tests/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot
deleted file mode 100644
index 19c220e2e1d..00000000000
--- a/tests/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot
+++ /dev/null
@@ -1,13 +0,0 @@
-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 align="left"/>    13:10-13:10: @5[0]: Coverage::Counter(1) 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(2) = Expression(1) + Zero 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=<0>];
-    bcb1__Cov_0_3 -> bcb2__Cov_0_3 [label=<otherwise>];
-    bcb0__Cov_0_3 -> bcb1__Cov_0_3 [label=<>];
-}
diff --git a/tests/mir-opt/coverage_graphviz.rs b/tests/mir-opt/coverage_graphviz.rs
deleted file mode 100644
index 09403bb3a79..00000000000
--- a/tests/mir-opt/coverage_graphviz.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Test that `-C instrument-coverage` with `-Z dump-mir-graphviz` generates a graphviz (.dot file)
-// rendering of the `BasicCoverageBlock` coverage control flow graph, with counters and
-// expressions.
-
-// needs-profiler-support
-// compile-flags: -C instrument-coverage -Z dump-mir-graphviz
-// EMIT_MIR coverage_graphviz.main.InstrumentCoverage.0.dot
-// EMIT_MIR coverage_graphviz.bar.InstrumentCoverage.0.dot
-fn main() {
-    loop {
-        if bar() {
-            break;
-        }
-    }
-}
-
-#[inline(never)]
-fn bar() -> bool {
-    true
-}
diff --git a/tests/mir-opt/dont_inline_type_id.call.Inline.diff b/tests/mir-opt/dont_inline_type_id.call.Inline.diff
new file mode 100644
index 00000000000..80cfe07b0cb
--- /dev/null
+++ b/tests/mir-opt/dont_inline_type_id.call.Inline.diff
@@ -0,0 +1,20 @@
+- // MIR for `call` before Inline
++ // MIR for `call` after Inline
+  
+  fn call(_1: &T) -> TypeId {
+      debug s => _1;
+      let mut _0: std::any::TypeId;
+      let mut _2: &T;
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = &(*_1);
+          _0 = <T as Any>::type_id(move _2) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dont_inline_type_id.rs b/tests/mir-opt/dont_inline_type_id.rs
new file mode 100644
index 00000000000..d8a56636094
--- /dev/null
+++ b/tests/mir-opt/dont_inline_type_id.rs
@@ -0,0 +1,15 @@
+// unit-test: Inline
+// compile-flags: --crate-type=lib -C panic=abort
+
+use std::any::Any;
+use std::any::TypeId;
+
+struct A<T: ?Sized + 'static> {
+    a: i32,
+    b: T,
+}
+
+// EMIT_MIR dont_inline_type_id.call.Inline.diff
+pub fn call<T: ?Sized + 'static>(s: &T) -> TypeId {
+    s.type_id()
+}
diff --git a/tests/mir-opt/inline_generically_if_sized.call.Inline.diff b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
new file mode 100644
index 00000000000..0cf4565dc99
--- /dev/null
+++ b/tests/mir-opt/inline_generically_if_sized.call.Inline.diff
@@ -0,0 +1,24 @@
+- // MIR for `call` before Inline
++ // MIR for `call` after Inline
+  
+  fn call(_1: &T) -> i32 {
+      debug s => _1;
+      let mut _0: i32;
+      let mut _2: &T;
++     scope 1 (inlined <T as Foo>::bar) {
++         debug self => _2;
++     }
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = &(*_1);
+-         _0 = <T as Foo>::bar(move _2) -> [return: bb1, unwind unreachable];
+-     }
+- 
+-     bb1: {
++         _0 = const 0_i32;
+          StorageDead(_2);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/inline_generically_if_sized.rs b/tests/mir-opt/inline_generically_if_sized.rs
new file mode 100644
index 00000000000..1acfff7a56b
--- /dev/null
+++ b/tests/mir-opt/inline_generically_if_sized.rs
@@ -0,0 +1,17 @@
+// unit-test: Inline
+// compile-flags: --crate-type=lib -C panic=abort
+
+trait Foo {
+    fn bar(&self) -> i32;
+}
+
+impl<T> Foo for T {
+    fn bar(&self) -> i32 {
+        0
+    }
+}
+
+// EMIT_MIR inline_generically_if_sized.call.Inline.diff
+pub fn call<T>(s: &T) -> i32 {
+    s.bar()
+}
diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir
index 1d3405a9afe..18a663d9f9e 100644
--- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-abort.mir
@@ -11,7 +11,7 @@ fn outer(_1: u8) -> u8 {
         _0 = inner(move _2) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/spans.rs:10:5: 10:14
                                          // mir::Constant
                                          // + span: $DIR/spans.rs:10:5: 10:10
-                                         // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(<ZST>) }
+                                         // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) }
     }
 
     bb1: {
diff --git a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir
index 285b6e41314..1c02fb72bcd 100644
--- a/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/spans.outer.PreCodegen.after.panic-unwind.mir
@@ -11,7 +11,7 @@ fn outer(_1: u8) -> u8 {
         _0 = inner(move _2) -> [return: bb1, unwind continue]; // scope 0 at $DIR/spans.rs:10:5: 10:14
                                          // mir::Constant
                                          // + span: $DIR/spans.rs:10:5: 10:10
-                                         // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(<ZST>) }
+                                         // + literal: Const { ty: for<'a> fn(&'a u8) -> u8 {inner}, val: Value(inner) }
     }
 
     bb1: {
diff --git a/tests/run-make/dump-ice-to-disk/check.sh b/tests/run-make/dump-ice-to-disk/check.sh
index 91109596a45..ab6f9ab6018 100644
--- a/tests/run-make/dump-ice-to-disk/check.sh
+++ b/tests/run-make/dump-ice-to-disk/check.sh
@@ -22,8 +22,8 @@ rm $TMPDIR/rustc-ice-*.txt
 # Explicitly disabling ICE dump
 export RUSTC_ICE=0
 $RUSTC src/lib.rs -Z treat-err-as-bug=1 1>$TMPDIR/rust-test-disabled.log 2>&1
-should_be_empty_tmp=$(ls -l $TMPDIR/rustc-ice-*.txt | wc -l)
-should_be_empty_dot=$(ls -l ./rustc-ice-*.txt | wc -l)
+should_be_empty_tmp=$(ls -l $TMPDIR/rustc-ice-*.txt 2>/dev/null | wc -l)
+should_be_empty_dot=$(ls -l ./rustc-ice-*.txt 2>/dev/null | wc -l)
 
 echo "#### ICE Dump content:"
 echo $content
diff --git a/tests/rustdoc-gui/search-corrections.goml b/tests/rustdoc-gui/search-corrections.goml
index 5d1b83b35c5..aeb3c9b31a3 100644
--- a/tests/rustdoc-gui/search-corrections.goml
+++ b/tests/rustdoc-gui/search-corrections.goml
@@ -54,3 +54,53 @@ assert-text: (
     ".search-corrections",
     "Type \"notablestructwithlongnamr\" not found. Showing results for closest type name \"notablestructwithlongname\" instead."
 )
+
+// Now, generic correction
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "NotableStructWithLongNamr, NotableStructWithLongNamr")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".search-corrections", {
+    "display": "block"
+})
+assert-text: (
+    ".search-corrections",
+    "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+)
+
+// Now, generic correction plus error
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "Foo<NotableStructWithLongNamr>,y")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".search-corrections", {
+    "display": "block"
+})
+assert-text: (
+    ".search-corrections",
+    "Type \"notablestructwithlongnamr\" not found and used as generic parameter. Consider searching for \"notablestructwithlongname\" instead."
+)
+
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Intentionally wrong spelling of "NotableStructWithLongName"
+write: (".search-input", "generic:NotableStructWithLongNamr<x>,y")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+assert-css: (".error", {
+    "display": "block"
+})
+assert-text: (
+    ".error",
+    "Query parser error: \"Generic type parameter notablestructwithlongnamr does not accept generic parameters\"."
+)
diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml
index 7bbde3ec23d..427201e1b5d 100644
--- a/tests/rustdoc-gui/search-tab.goml
+++ b/tests/rustdoc-gui/search-tab.goml
@@ -1,5 +1,5 @@
 // Checking the colors of the search tab headers.
-go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html?search=something"
+go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html?search=foo"
 show-text: true
 
 define-function: (
@@ -74,3 +74,87 @@ call-function: ("check-colors", {
     "border_top_selected": "2px solid #0089ff",
     "border_top_hover": "2px solid #0089ff",
 })
+
+// set size wide enough that the text is in a single row
+set-window-size: (851, 600)
+
+// Check the size and count in tabs
+assert-text: ("#search-tabs > button:nth-child(1) > .count", " (23) ")
+assert-text: ("#search-tabs > button:nth-child(2) > .count", " (4)  ")
+assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ")
+store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth})
+assert-property: ("#search-tabs > button:nth-child(2)", {"offsetWidth": |buttonWidth|})
+assert-property: ("#search-tabs > button:nth-child(3)", {"offsetWidth": |buttonWidth|})
+store-property: ("#search-tabs > button:nth-child(1) > .count", {"offsetWidth": countWidth})
+assert-property: ("#search-tabs > button:nth-child(2) > .count", {"offsetWidth": |countWidth|})
+assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth": |countWidth|})
+
+// Check that counts are in a row with each other
+compare-elements-position: (
+    "#search-tabs > button:nth-child(1) > .count",
+    "#search-tabs > button:nth-child(2) > .count",
+    ("y")
+)
+compare-elements-position: (
+    "#search-tabs > button:nth-child(2) > .count",
+    "#search-tabs > button:nth-child(3) > .count",
+    ("y")
+)
+// Check that counts are beside the titles and haven't wrapped
+compare-elements-position-near: (
+    "#search-tabs > button:nth-child(1)",
+    "#search-tabs > button:nth-child(1) > .count",
+    {"y": 8}
+)
+compare-elements-position-near: (
+    "#search-tabs > button:nth-child(2)",
+    "#search-tabs > button:nth-child(2) > .count",
+    {"y": 8}
+)
+compare-elements-position-near: (
+    "#search-tabs > button:nth-child(2)",
+    "#search-tabs > button:nth-child(2) > .count",
+    {"y": 8}
+)
+
+// Set size narrow enough that they wrap.
+// When I tested it, it wrapped at 811px, but I added some fudge factor to ensure it
+// doesn't prematurely wrap with slightly different font kerning or whatever, with a
+// @media query
+set-window-size: (850, 600)
+
+// all counts and buttons still have same size
+store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth})
+assert-property: ("#search-tabs > button:nth-child(2)", {"offsetWidth": |buttonWidth|})
+assert-property: ("#search-tabs > button:nth-child(3)", {"offsetWidth": |buttonWidth|})
+store-property: ("#search-tabs > button:nth-child(1) > .count", {"offsetWidth": countWidth})
+assert-property: ("#search-tabs > button:nth-child(2) > .count", {"offsetWidth": |countWidth|})
+assert-property: ("#search-tabs > button:nth-child(3) > .count", {"offsetWidth": |countWidth|})
+
+// Check that counts are still in a row with each other
+compare-elements-position: (
+    "#search-tabs > button:nth-child(1) > .count",
+    "#search-tabs > button:nth-child(2) > .count",
+    ("y")
+)
+compare-elements-position: (
+    "#search-tabs > button:nth-child(2) > .count",
+    "#search-tabs > button:nth-child(3) > .count",
+    ("y")
+)
+// Check that counts are NOT beside the titles; now they have wrapped
+compare-elements-position-near-false: (
+    "#search-tabs > button:nth-child(1)",
+    "#search-tabs > button:nth-child(1) > .count",
+    {"y": 8}
+)
+compare-elements-position-near-false: (
+    "#search-tabs > button:nth-child(2)",
+    "#search-tabs > button:nth-child(2) > .count",
+    {"y": 8}
+)
+compare-elements-position-near-false: (
+    "#search-tabs > button:nth-child(2)",
+    "#search-tabs > button:nth-child(2) > .count",
+    {"y": 8}
+)
diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js
index 25997850661..e154fa707ab 100644
--- a/tests/rustdoc-js-std/option-type-signatures.js
+++ b/tests/rustdoc-js-std/option-type-signatures.js
@@ -1,3 +1,7 @@
+// ignore-order
+
+const FILTER_CRATE = "std";
+
 const EXPECTED = [
     {
         'query': 'option, fnonce -> option',
@@ -19,4 +23,62 @@ const EXPECTED = [
             { 'path': 'std::option::Option', 'name': 'as_mut_slice' },
         ],
     },
+    {
+        'query': 'option<t>, option<t> -> option<t>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'or' },
+            { 'path': 'std::option::Option', 'name': 'xor' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<u> -> option<u>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'and' },
+            { 'path': 'std::option::Option', 'name': 'zip' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<u> -> option<t>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'and' },
+            { 'path': 'std::option::Option', 'name': 'zip' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<u> -> option<t, u>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'zip' },
+        ],
+    },
+    {
+        'query': 'option<t>, e -> result<t, e>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'ok_or' },
+            { 'path': 'std::result::Result', 'name': 'transpose' },
+        ],
+    },
+    {
+        'query': 'result<option<t>, e> -> option<result<t, e>>',
+        'others': [
+            { 'path': 'std::result::Result', 'name': 'transpose' },
+        ],
+    },
+    {
+        'query': 'option<t>, option<t> -> bool',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'eq' },
+        ],
+    },
+    {
+        'query': 'option<option<t>> -> option<t>',
+        'others': [
+            { 'path': 'std::option::Option', 'name': 'flatten' },
+        ],
+    },
+    {
+        'query': 'option<t>',
+        'returned': [
+            { 'path': 'std::result::Result', 'name': 'ok' },
+        ],
+    },
 ];
diff --git a/tests/rustdoc-js-std/vec-type-signatures.js b/tests/rustdoc-js-std/vec-type-signatures.js
new file mode 100644
index 00000000000..18cf9d6efd0
--- /dev/null
+++ b/tests/rustdoc-js-std/vec-type-signatures.js
@@ -0,0 +1,22 @@
+// ignore-order
+
+const FILTER_CRATE = "std";
+
+const EXPECTED = [
+    {
+        'query': 'vec::intoiter<T> -> [T]',
+        'others': [
+            { 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
+        ],
+    },
+    {
+        'query': 'vec::intoiter<T> -> []',
+        'others': [
+            { 'path': 'std::vec::IntoIter', 'name': 'as_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'as_mut_slice' },
+            { 'path': 'std::vec::IntoIter', 'name': 'next_chunk' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/generics-match-ambiguity.js b/tests/rustdoc-js/generics-match-ambiguity.js
index a9932a16ca3..edce4268c5a 100644
--- a/tests/rustdoc-js/generics-match-ambiguity.js
+++ b/tests/rustdoc-js/generics-match-ambiguity.js
@@ -79,6 +79,7 @@ const EXPECTED = [
             { 'path': 'generics_match_ambiguity', 'name': 'baac' },
             { 'path': 'generics_match_ambiguity', 'name': 'baaf' },
             { 'path': 'generics_match_ambiguity', 'name': 'baag' },
+            { 'path': 'generics_match_ambiguity', 'name': 'baah' },
         ],
     },
     {
diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js
index 4ccfb8f4e4d..a71393b5e05 100644
--- a/tests/rustdoc-js/generics-trait.js
+++ b/tests/rustdoc-js/generics-trait.js
@@ -12,12 +12,16 @@ const EXPECTED = [
         ],
     },
     {
-        'query': 'Result<SomeTraiz>',
-        'correction': null,
+        'query': 'Resulx<SomeTrait>',
         'in_args': [],
         'returned': [],
     },
     {
+        'query': 'Result<SomeTraiz>',
+        'proposeCorrectionFrom': 'SomeTraiz',
+        'proposeCorrectionTo': 'SomeTrait',
+    },
+    {
         'query': 'OtherThingxxxxxxxx',
         'correction': null,
         'in_args': [
diff --git a/tests/rustdoc-js/generics-unbox.js b/tests/rustdoc-js/generics-unbox.js
new file mode 100644
index 00000000000..9cdfc7ac8b6
--- /dev/null
+++ b/tests/rustdoc-js/generics-unbox.js
@@ -0,0 +1,38 @@
+// exact-check
+
+const EXPECTED = [
+    {
+        'query': 'Inside<T> -> Out1<T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'alpha' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out3<T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out4<T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out3<U, T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+    {
+        'query': 'Inside<T> -> Out4<U, T>',
+        'others': [
+            { 'path': 'generics_unbox', 'name': 'beta' },
+            { 'path': 'generics_unbox', 'name': 'gamma' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/generics-unbox.rs b/tests/rustdoc-js/generics-unbox.rs
new file mode 100644
index 00000000000..bef34f891e9
--- /dev/null
+++ b/tests/rustdoc-js/generics-unbox.rs
@@ -0,0 +1,36 @@
+pub struct Out<A, B = ()> {
+    a: A,
+    b: B,
+}
+
+pub struct Out1<A, const N: usize> {
+    a: [A; N],
+}
+
+pub struct Out2<A, const N: usize> {
+    a: [A; N],
+}
+
+pub struct Out3<A, B> {
+    a: A,
+    b: B,
+}
+
+pub struct Out4<A, B> {
+    a: A,
+    b: B,
+}
+
+pub struct Inside<T>(T);
+
+pub fn alpha<const N: usize, T>(_: Inside<T>) -> Out<Out1<T, N>, Out2<T, N>> {
+    loop {}
+}
+
+pub fn beta<T, U>(_: Inside<T>) -> Out<Out3<T, U>, Out4<U, T>> {
+    loop {}
+}
+
+pub fn gamma<T, U>(_: Inside<T>) -> Out<Out3<U, T>, Out4<T, U>> {
+    loop {}
+}
diff --git a/tests/rustdoc-js/type-parameters.js b/tests/rustdoc-js/type-parameters.js
new file mode 100644
index 00000000000..e695f189bb6
--- /dev/null
+++ b/tests/rustdoc-js/type-parameters.js
@@ -0,0 +1,87 @@
+// exact-check
+// ignore-order
+
+const EXPECTED = [
+    {
+        query: '-> trait:Some',
+        others: [
+            { path: 'foo', name: 'alef' },
+            { path: 'foo', name: 'alpha' },
+        ],
+    },
+    {
+        query: '-> generic:T',
+        others: [
+            { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'alef' },
+            { path: 'foo', name: 'beta' },
+        ],
+    },
+    {
+        query: 'A -> B',
+        others: [
+            { path: 'foo', name: 'bet' },
+        ],
+    },
+    {
+        query: 'A -> A',
+        others: [
+            { path: 'foo', name: 'beta' },
+        ],
+    },
+    {
+        query: 'A, A',
+        others: [
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'A, B',
+        others: [
+            { path: 'foo', name: 'other' },
+        ],
+    },
+    {
+        query: 'Other, Other',
+        others: [
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'generic:T',
+        in_args: [
+            { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'beta' },
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'generic:Other',
+        in_args: [
+            { path: 'foo', name: 'bet' },
+            { path: 'foo', name: 'beta' },
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'trait:Other',
+        in_args: [
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'Other',
+        in_args: [
+            { path: 'foo', name: 'other' },
+            { path: 'foo', name: 'alternate' },
+        ],
+    },
+    {
+        query: 'trait:T',
+        in_args: [],
+    },
+];
diff --git a/tests/rustdoc-js/type-parameters.rs b/tests/rustdoc-js/type-parameters.rs
new file mode 100644
index 00000000000..cda5e26171f
--- /dev/null
+++ b/tests/rustdoc-js/type-parameters.rs
@@ -0,0 +1,15 @@
+#![crate_name="foo"]
+
+pub trait Some {}
+impl Some for () {}
+pub trait Other {}
+impl Other for () {}
+
+pub fn alef<T: Some>() -> T { loop {} }
+pub fn alpha() -> impl Some { }
+
+pub fn bet<T, U>(t: T) -> U { loop {} }
+pub fn beta<T>(t: T) -> T {}
+
+pub fn other<T: Other, U: Other>(t: T, u: U) { loop {} }
+pub fn alternate<T: Other>(t: T, u: T) { loop {} }
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
index dd8759b7e37..5398d5833c7 100644
--- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.rs
@@ -57,25 +57,23 @@ pub fn foo8() {}
 /// ```{class=one=two}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `=`
+//~^^^ ERROR unexpected `=` character
 pub fn foo9() {}
 
 /// ```{.one.two}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `.` character
 pub fn foo10() {}
 
-/// ```{class=.one}
+/// ```{class=(one}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `.` character after `=`
+//~^^^ ERROR unexpected `(` character after `=`
 pub fn foo11() {}
 
 /// ```{class=one.two}
 /// main;
 /// ```
-//~^^^ ERROR unexpected `.` character
 pub fn foo12() {}
 
 /// ```{(comment)}
diff --git a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
index 3e0dc4b2f7a..14b4b3bab3f 100644
--- a/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
+++ b/tests/rustdoc-ui/custom_code_classes_in_docs-warning.stderr
@@ -77,37 +77,21 @@ LL | | /// main;
 LL | | /// ```
    | |_______^
 
-error: unexpected `.` character
-  --> $DIR/custom_code_classes_in_docs-warning.rs:63:1
+error: unexpected `(` character after `=`
+  --> $DIR/custom_code_classes_in_docs-warning.rs:68:1
    |
-LL | / /// ```{.one.two}
-LL | | /// main;
-LL | | /// ```
-   | |_______^
-
-error: unexpected `.` character after `=`
-  --> $DIR/custom_code_classes_in_docs-warning.rs:69:1
-   |
-LL | / /// ```{class=.one}
-LL | | /// main;
-LL | | /// ```
-   | |_______^
-
-error: unexpected `.` character
-  --> $DIR/custom_code_classes_in_docs-warning.rs:75:1
-   |
-LL | / /// ```{class=one.two}
+LL | / /// ```{class=(one}
 LL | | /// main;
 LL | | /// ```
    | |_______^
 
 error: unexpected character `(`
-  --> $DIR/custom_code_classes_in_docs-warning.rs:81:1
+  --> $DIR/custom_code_classes_in_docs-warning.rs:79:1
    |
 LL | / /// ```{(comment)}
 LL | | /// main;
 LL | | /// ```
    | |_______^
 
-error: aborting due to 13 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
index 3f0f8b5b9f9..99263a944f8 100644
--- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
@@ -8,3 +8,8 @@
 //~| NOTE see issue #79483 <https://github.com/rust-lang/rust/issues/79483>
 //~| HELP add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
 pub struct Bar;
+
+/// ```ASN.1
+/// int main(void) { return 0; }
+/// ```
+pub struct Bar2;
diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs
index b3e75bb8233..249e8176283 100644
--- a/tests/ui/abi/compatibility.rs
+++ b/tests/ui/abi/compatibility.rs
@@ -10,8 +10,6 @@ use std::ptr::NonNull;
 // Hence there are `cfg` throughout this test to disable parts of it on those targets.
 // sparc64: https://github.com/rust-lang/rust/issues/115336
 // mips64: https://github.com/rust-lang/rust/issues/115404
-// riscv64: https://github.com/rust-lang/rust/issues/115481
-// loongarch64: https://github.com/rust-lang/rust/issues/115509
 
 macro_rules! assert_abi_compatible {
     ($name:ident, $t1:ty, $t2:ty) => {
@@ -110,7 +108,6 @@ macro_rules! test_transparent {
             test_abi_compatible!(wrap1, $t, Wrapper1<$t>);
             test_abi_compatible!(wrap2, $t, Wrapper2<$t>);
             test_abi_compatible!(wrap3, $t, Wrapper3<$t>);
-            #[cfg(not(any(target_arch = "riscv64", target_arch = "loongarch64")))]
             test_abi_compatible!(wrap4, $t, WrapperUnion<$t>);
         }
     };
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
new file mode 100644
index 00000000000..85d17ddff94
--- /dev/null
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
@@ -0,0 +1,38 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
+
+#![feature(async_fn_in_trait)]
+
+fn main() {
+    let _ = async {
+        A.first().await.second().await;
+    };
+}
+
+pub trait First {
+    type Second: Second;
+    async fn first(self) -> Self::Second;
+}
+
+struct A;
+
+impl First for A {
+    type Second = A;
+    async fn first(self) -> Self::Second {
+        A
+    }
+}
+
+pub trait Second {
+    async fn second(self);
+}
+
+impl<C> Second for C
+where
+    C: First,
+{
+    async fn second(self) {
+        self.first().await.second().await;
+    }
+}
diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
new file mode 100644
index 00000000000..3f487a6e5fe
--- /dev/null
+++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<A as Second>::{opaque#0} == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/async-await/issues/issue-66695-static-refs.rs b/tests/ui/async-await/issues/issue-66695-static-refs.rs
index f0609713b4d..1b0e1c6c9e7 100644
--- a/tests/ui/async-await/issues/issue-66695-static-refs.rs
+++ b/tests/ui/async-await/issues/issue-66695-static-refs.rs
@@ -1,12 +1,15 @@
 // build-pass
 // edition:2018
 
+#![feature(if_let_guard)]
+
 static A: [i32; 5] = [1, 2, 3, 4, 5];
 
 async fn fun() {
     let u = A[async { 1 }.await];
     match A {
         i if async { true }.await => (),
+        i if let Some(1) = async { Some(1) }.await => (),
         _ => (),
     }
 }
@@ -18,6 +21,7 @@ fn main() {
     async {
         match A {
             i if async { true }.await => (),
+            i if let Some(2) = async { Some(2) }.await => (),
             _ => (),
         }
     };
diff --git a/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs b/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs
index c4f8f607d25..80d824d3b2e 100644
--- a/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs
+++ b/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs
@@ -5,6 +5,8 @@
 // [drop_tracking] compile-flags: -Zdrop-tracking
 // [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
 
+#![feature(if_let_guard)]
+
 static mut A: [i32; 5] = [1, 2, 3, 4, 5];
 
 fn is_send_sync<T: Send + Sync>(_: T) {}
@@ -14,6 +16,7 @@ async fn fun() {
     unsafe {
         match A {
             i if async { true }.await => (),
+            i if let Some(1) = async { Some(1) }.await => (),
             _ => (),
         }
     }
@@ -27,6 +30,7 @@ fn main() {
         unsafe {
             match A {
                 i if async { true }.await => (),
+                i if let Some(2) = async { Some(2) }.await => (),
                 _ => (),
             }
         }
diff --git a/tests/ui/async-await/missed-capture-issue-107414.rs b/tests/ui/async-await/missed-capture-issue-107414.rs
index 0ab4f5ade98..bb14eb74b3a 100644
--- a/tests/ui/async-await/missed-capture-issue-107414.rs
+++ b/tests/ui/async-await/missed-capture-issue-107414.rs
@@ -1,6 +1,8 @@
 // check-pass
 // edition:2018
 
+#![feature(if_let_guard)]
+
 fn main() {}
 
 struct StructA {}
@@ -22,3 +24,10 @@ async fn ice() {
         _ => {}
     }
 }
+
+async fn if_let() {
+    match Some(StructB {}) {
+        Some(struct_b) if let true = get_struct_a_async().await.fn_taking_struct_b(&struct_b) => {}
+        _ => {}
+    }
+}
diff --git a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
index 3b63ec45804..8f45902035e 100644
--- a/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
+++ b/tests/ui/async-await/return-type-notation/normalizing-self-auto-trait-issue-109924.current.stderr
@@ -16,6 +16,14 @@ LL |     build(Bar);
    |     required by a bound introduced by this call
    |
    = help: the trait `for<'a> Send` is not implemented for `impl Future<Output = ()> { <_ as Foo>::bar() }`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/normalizing-self-auto-trait-issue-109924.rs:23:11
+   |
+LL |     build(Bar);
+   |     ------^^^-
+   |     |     |
+   |     |     the trait solver is unable to infer the generic types that should be inferred from this argument
+   |     add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `build`
   --> $DIR/normalizing-self-auto-trait-issue-109924.rs:20:39
    |
diff --git a/tests/ui/binding/match-beginning-vert.rs b/tests/ui/binding/match-beginning-vert.rs
index 79267400b28..93c08f0b710 100644
--- a/tests/ui/binding/match-beginning-vert.rs
+++ b/tests/ui/binding/match-beginning-vert.rs
@@ -1,4 +1,7 @@
 // run-pass
+
+#![feature(if_let_guard)]
+
 enum Foo {
     A,
     B,
@@ -13,6 +16,7 @@ fn main() {
         match *foo {
             | A => println!("A"),
             | B | C if 1 < 2 => println!("BC!"),
+            | D if let 1 = 1 => println!("D!"),
             | _ => {},
         }
     }
diff --git a/tests/ui/coherence/coherence-overlap-downstream-inherent.stderr b/tests/ui/coherence/coherence-overlap-downstream-inherent.next.stderr
index bbce4b530b4..2938bc629b2 100644
--- a/tests/ui/coherence/coherence-overlap-downstream-inherent.stderr
+++ b/tests/ui/coherence/coherence-overlap-downstream-inherent.next.stderr
@@ -1,5 +1,5 @@
 error[E0592]: duplicate definitions with name `dummy`
-  --> $DIR/coherence-overlap-downstream-inherent.rs:7:26
+  --> $DIR/coherence-overlap-downstream-inherent.rs:10:26
    |
 LL | impl<T:Sugar> Sweet<T> { fn dummy(&self) { } }
    |                          ^^^^^^^^^^^^^^^ duplicate definitions for `dummy`
@@ -8,7 +8,7 @@ LL | impl<T:Fruit> Sweet<T> { fn dummy(&self) { } }
    |                          --------------- other definition for `dummy`
 
 error[E0592]: duplicate definitions with name `f`
-  --> $DIR/coherence-overlap-downstream-inherent.rs:13:38
+  --> $DIR/coherence-overlap-downstream-inherent.rs:16:38
    |
 LL | impl<X, T> A<T, X> where T: Bar<X> { fn f(&self) {} }
    |                                      ^^^^^^^^^^^ duplicate definitions for `f`
diff --git a/tests/ui/coherence/coherence-overlap-downstream-inherent.old.stderr b/tests/ui/coherence/coherence-overlap-downstream-inherent.old.stderr
new file mode 100644
index 00000000000..2938bc629b2
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-downstream-inherent.old.stderr
@@ -0,0 +1,23 @@
+error[E0592]: duplicate definitions with name `dummy`
+  --> $DIR/coherence-overlap-downstream-inherent.rs:10:26
+   |
+LL | impl<T:Sugar> Sweet<T> { fn dummy(&self) { } }
+   |                          ^^^^^^^^^^^^^^^ duplicate definitions for `dummy`
+LL |
+LL | impl<T:Fruit> Sweet<T> { fn dummy(&self) { } }
+   |                          --------------- other definition for `dummy`
+
+error[E0592]: duplicate definitions with name `f`
+  --> $DIR/coherence-overlap-downstream-inherent.rs:16:38
+   |
+LL | impl<X, T> A<T, X> where T: Bar<X> { fn f(&self) {} }
+   |                                      ^^^^^^^^^^^ duplicate definitions for `f`
+LL |
+LL | impl<X> A<i32, X> { fn f(&self) {} }
+   |                     ----------- other definition for `f`
+   |
+   = note: downstream crates may implement trait `Bar<_>` for type `i32`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0592`.
diff --git a/tests/ui/coherence/coherence-overlap-downstream-inherent.rs b/tests/ui/coherence/coherence-overlap-downstream-inherent.rs
index 5dea33e330b..2c3ef4fd3f7 100644
--- a/tests/ui/coherence/coherence-overlap-downstream-inherent.rs
+++ b/tests/ui/coherence/coherence-overlap-downstream-inherent.rs
@@ -1,3 +1,6 @@
+// revisions: old next
+//[next] compile-flags: -Ztrait-solver=next
+
 // Tests that we consider `T: Sugar + Fruit` to be ambiguous, even
 // though no impls are found.
 
diff --git a/tests/ui/coherence/coherence-overlap-downstream.stderr b/tests/ui/coherence/coherence-overlap-downstream.next.stderr
index 7f373e595a3..9d62efbc315 100644
--- a/tests/ui/coherence/coherence-overlap-downstream.stderr
+++ b/tests/ui/coherence/coherence-overlap-downstream.next.stderr
@@ -1,5 +1,5 @@
 error[E0119]: conflicting implementations of trait `Sweet`
-  --> $DIR/coherence-overlap-downstream.rs:8:1
+  --> $DIR/coherence-overlap-downstream.rs:11:1
    |
 LL | impl<T:Sugar> Sweet for T { }
    | ------------------------- first implementation here
@@ -7,7 +7,7 @@ LL | impl<T:Fruit> Sweet for T { }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
 
 error[E0119]: conflicting implementations of trait `Foo<_>` for type `i32`
-  --> $DIR/coherence-overlap-downstream.rs:14:1
+  --> $DIR/coherence-overlap-downstream.rs:17:1
    |
 LL | impl<X, T> Foo<X> for T where T: Bar<X> {}
    | ----------------------- first implementation here
diff --git a/tests/ui/coherence/coherence-overlap-downstream.old.stderr b/tests/ui/coherence/coherence-overlap-downstream.old.stderr
new file mode 100644
index 00000000000..9d62efbc315
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-downstream.old.stderr
@@ -0,0 +1,21 @@
+error[E0119]: conflicting implementations of trait `Sweet`
+  --> $DIR/coherence-overlap-downstream.rs:11:1
+   |
+LL | impl<T:Sugar> Sweet for T { }
+   | ------------------------- first implementation here
+LL | impl<T:Fruit> Sweet for T { }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
+
+error[E0119]: conflicting implementations of trait `Foo<_>` for type `i32`
+  --> $DIR/coherence-overlap-downstream.rs:17:1
+   |
+LL | impl<X, T> Foo<X> for T where T: Bar<X> {}
+   | ----------------------- first implementation here
+LL | impl<X> Foo<X> for i32 {}
+   | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `i32`
+   |
+   = note: downstream crates may implement trait `Bar<_>` for type `i32`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/coherence-overlap-downstream.rs b/tests/ui/coherence/coherence-overlap-downstream.rs
index 738ec0e3d45..a4e559604a0 100644
--- a/tests/ui/coherence/coherence-overlap-downstream.rs
+++ b/tests/ui/coherence/coherence-overlap-downstream.rs
@@ -1,3 +1,6 @@
+// revisions: old next
+//[next] compile-flags: -Ztrait-solver=next
+
 // Tests that we consider `T: Sugar + Fruit` to be ambiguous, even
 // though no impls are found.
 
diff --git a/tests/ui/coherence/coherence-overlap-issue-23516-inherent.stderr b/tests/ui/coherence/coherence-overlap-issue-23516-inherent.next.stderr
index 3ad818cbc36..c02a679c149 100644
--- a/tests/ui/coherence/coherence-overlap-issue-23516-inherent.stderr
+++ b/tests/ui/coherence/coherence-overlap-issue-23516-inherent.next.stderr
@@ -1,5 +1,5 @@
 error[E0592]: duplicate definitions with name `dummy`
-  --> $DIR/coherence-overlap-issue-23516-inherent.rs:9:25
+  --> $DIR/coherence-overlap-issue-23516-inherent.rs:12:25
    |
 LL | impl<T:Sugar> Cake<T> { fn dummy(&self) { } }
    |                         ^^^^^^^^^^^^^^^ duplicate definitions for `dummy`
diff --git a/tests/ui/coherence/coherence-overlap-issue-23516-inherent.old.stderr b/tests/ui/coherence/coherence-overlap-issue-23516-inherent.old.stderr
new file mode 100644
index 00000000000..c02a679c149
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-issue-23516-inherent.old.stderr
@@ -0,0 +1,14 @@
+error[E0592]: duplicate definitions with name `dummy`
+  --> $DIR/coherence-overlap-issue-23516-inherent.rs:12:25
+   |
+LL | impl<T:Sugar> Cake<T> { fn dummy(&self) { } }
+   |                         ^^^^^^^^^^^^^^^ duplicate definitions for `dummy`
+LL |
+LL | impl<U:Sugar> Cake<Box<U>> { fn dummy(&self) { } }
+   |                              --------------- other definition for `dummy`
+   |
+   = note: downstream crates may implement trait `Sugar` for type `std::boxed::Box<_>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0592`.
diff --git a/tests/ui/coherence/coherence-overlap-issue-23516-inherent.rs b/tests/ui/coherence/coherence-overlap-issue-23516-inherent.rs
index a272e620fca..a7c90a6b8c8 100644
--- a/tests/ui/coherence/coherence-overlap-issue-23516-inherent.rs
+++ b/tests/ui/coherence/coherence-overlap-issue-23516-inherent.rs
@@ -1,3 +1,6 @@
+// revisions: old next
+//[next] compile-flags: -Ztrait-solver=next
+
 // Tests that we consider `Box<U>: !Sugar` to be ambiguous, even
 // though we see no impl of `Sugar` for `Box`. Therefore, an overlap
 // error is reported for the following pair of impls (#23516).
diff --git a/tests/ui/coherence/coherence-overlap-issue-23516.stderr b/tests/ui/coherence/coherence-overlap-issue-23516.next.stderr
index cd398426704..a4e87af8ac4 100644
--- a/tests/ui/coherence/coherence-overlap-issue-23516.stderr
+++ b/tests/ui/coherence/coherence-overlap-issue-23516.next.stderr
@@ -1,5 +1,5 @@
 error[E0119]: conflicting implementations of trait `Sweet` for type `Box<_>`
-  --> $DIR/coherence-overlap-issue-23516.rs:8:1
+  --> $DIR/coherence-overlap-issue-23516.rs:11:1
    |
 LL | impl<T:Sugar> Sweet for T { }
    | ------------------------- first implementation here
diff --git a/tests/ui/coherence/coherence-overlap-issue-23516.old.stderr b/tests/ui/coherence/coherence-overlap-issue-23516.old.stderr
new file mode 100644
index 00000000000..a4e87af8ac4
--- /dev/null
+++ b/tests/ui/coherence/coherence-overlap-issue-23516.old.stderr
@@ -0,0 +1,13 @@
+error[E0119]: conflicting implementations of trait `Sweet` for type `Box<_>`
+  --> $DIR/coherence-overlap-issue-23516.rs:11:1
+   |
+LL | impl<T:Sugar> Sweet for T { }
+   | ------------------------- first implementation here
+LL | impl<U:Sugar> Sweet for Box<U> { }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>`
+   |
+   = note: downstream crates may implement trait `Sugar` for type `std::boxed::Box<_>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/coherence-overlap-issue-23516.rs b/tests/ui/coherence/coherence-overlap-issue-23516.rs
index 63e42e8f412..c846d39716b 100644
--- a/tests/ui/coherence/coherence-overlap-issue-23516.rs
+++ b/tests/ui/coherence/coherence-overlap-issue-23516.rs
@@ -1,3 +1,6 @@
+// revisions: old next
+//[next] compile-flags: -Ztrait-solver=next
+
 // Tests that we consider `Box<U>: !Sugar` to be ambiguous, even
 // though we see no impl of `Sugar` for `Box`. Therefore, an overlap
 // error is reported for the following pair of impls (#23516).
diff --git a/tests/ui/coherence/inter-crate-ambiguity-causes-notes.stderr b/tests/ui/coherence/inter-crate-ambiguity-causes-notes.next.stderr
index 4ddd712b27c..0dd28706e07 100644
--- a/tests/ui/coherence/inter-crate-ambiguity-causes-notes.stderr
+++ b/tests/ui/coherence/inter-crate-ambiguity-causes-notes.next.stderr
@@ -1,5 +1,5 @@
 error[E0119]: conflicting implementations of trait `From<()>` for type `S`
-  --> $DIR/inter-crate-ambiguity-causes-notes.rs:9:1
+  --> $DIR/inter-crate-ambiguity-causes-notes.rs:12:1
    |
 LL | impl From<()> for S {
    | ------------------- first implementation here
diff --git a/tests/ui/coherence/inter-crate-ambiguity-causes-notes.old.stderr b/tests/ui/coherence/inter-crate-ambiguity-causes-notes.old.stderr
new file mode 100644
index 00000000000..0dd28706e07
--- /dev/null
+++ b/tests/ui/coherence/inter-crate-ambiguity-causes-notes.old.stderr
@@ -0,0 +1,14 @@
+error[E0119]: conflicting implementations of trait `From<()>` for type `S`
+  --> $DIR/inter-crate-ambiguity-causes-notes.rs:12:1
+   |
+LL | impl From<()> for S {
+   | ------------------- first implementation here
+...
+LL | impl<I> From<I> for S
+   | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `S`
+   |
+   = note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `()` in future versions
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/inter-crate-ambiguity-causes-notes.rs b/tests/ui/coherence/inter-crate-ambiguity-causes-notes.rs
index 5b11c78ab26..743e80d3f18 100644
--- a/tests/ui/coherence/inter-crate-ambiguity-causes-notes.rs
+++ b/tests/ui/coherence/inter-crate-ambiguity-causes-notes.rs
@@ -1,3 +1,6 @@
+// revisions: old next
+//[next] compile-flags: -Ztrait-solver=next
+
 struct S;
 
 impl From<()> for S {
diff --git a/tests/ui/const-generics/late-bound-vars/in_closure.rs b/tests/ui/const-generics/late-bound-vars/in_closure.rs
index 6549cad629b..443c755c601 100644
--- a/tests/ui/const-generics/late-bound-vars/in_closure.rs
+++ b/tests/ui/const-generics/late-bound-vars/in_closure.rs
@@ -1,23 +1,5 @@
-// failure-status: 1
 // known-bug: unknown
-// error-pattern:internal compiler error
-// normalize-stderr-test "internal compiler error.*" -> ""
-// normalize-stderr-test "DefId\([^)]*\)" -> "..."
-// normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> ""
-// normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> ""
-// normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> ""
-// normalize-stderr-test "note: compiler flags.*\n\n" -> ""
-// normalize-stderr-test "note: rustc.*running on.*\n\n" -> ""
-// normalize-stderr-test "thread.*panicked.*:\n.*\n" -> ""
-// normalize-stderr-test "stack backtrace:\n" -> ""
-// normalize-stderr-test "\s\d{1,}: .*\n" -> ""
-// normalize-stderr-test "\s at .*\n" -> ""
-// normalize-stderr-test ".*note: Some details.*\n" -> ""
-// normalize-stderr-test "\n[ ]*\n" -> ""
-// normalize-stderr-test "compiler/.*: projection" -> "projection"
-// normalize-stderr-test ".*omitted \d{1,} frame.*\n" -> ""
-// normalize-stderr-test "error: [\s\n]*query stack during panic:\n" -> ""
-// this should run-pass
+// see comment on `tests/ui/const-generics/late-bound-vars/simple.rs`
 
 #![feature(generic_const_exprs)]
 #![allow(incomplete_features)]
diff --git a/tests/ui/const-generics/late-bound-vars/in_closure.stderr b/tests/ui/const-generics/late-bound-vars/in_closure.stderr
index ac406bf2bb1..e15496454a0 100644
--- a/tests/ui/const-generics/late-bound-vars/in_closure.stderr
+++ b/tests/ui/const-generics/late-bound-vars/in_closure.stderr
@@ -1,10 +1,20 @@
-#0 [mir_borrowck] borrow-checking `test::{closure#0}::{constant#1}`
-#1 [mir_drops_elaborated_and_const_checked] elaborating drops for `test::{closure#0}::{constant#1}`
-#2 [mir_for_ctfe] caching mir of `test::{closure#0}::{constant#1}` for CTFE
-#3 [eval_to_allocation_raw] const-evaluating + checking `test::{closure#0}::{constant#1}`
-#4 [eval_to_allocation_raw] const-evaluating + checking `test::{closure#0}::{constant#1}`
-#5 [eval_to_valtree] evaluating type-level constant
-#6 [typeck] type-checking `test`
-#7 [analysis] running analysis passes on this crate
-end of query stack
-error: aborting due to previous error
\ No newline at end of file
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/in_closure.rs:16:29
+   |
+LL | fn test<'a>() {
+   |         -- lifetime defined here
+LL |     let _ = || {
+LL |         let _: [u8; inner::<'a>()];
+   |                             ^^
+
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/in_closure.rs:17:29
+   |
+LL | fn test<'a>() {
+   |         -- lifetime defined here
+...
+LL |         let _ = [0; inner::<'a>()];
+   |                             ^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.rs b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.rs
new file mode 100644
index 00000000000..b81aa50d9a9
--- /dev/null
+++ b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.rs
@@ -0,0 +1,13 @@
+// known-bug: unknown
+// see comment on `tests/ui/const-generics/late-bound-vars/simple.rs`
+
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+trait MyTrait<T> {}
+
+fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> {
+    todo!()
+}
+
+fn main() {}
diff --git a/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr
new file mode 100644
index 00000000000..21c8fe6865c
--- /dev/null
+++ b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr
@@ -0,0 +1,8 @@
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/late-bound-in-return-issue-77357.rs:9:53
+   |
+LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> {
+   |        -- lifetime defined here                     ^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.rs b/tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.rs
new file mode 100644
index 00000000000..89f01748fc9
--- /dev/null
+++ b/tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.rs
@@ -0,0 +1,15 @@
+// known-bug: unknown
+// see comment on `tests/ui/const-generics/late-bound-vars/simple.rs`
+
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+fn bug<'a>()
+where
+    for<'b> [(); {
+        let x: &'b ();
+        0
+    }]:
+{}
+
+fn main() {}
diff --git a/tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.stderr b/tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.stderr
new file mode 100644
index 00000000000..a66dc8db914
--- /dev/null
+++ b/tests/ui/const-generics/late-bound-vars/late-bound-in-where-issue-83993.stderr
@@ -0,0 +1,10 @@
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/late-bound-in-where-issue-83993.rs:10:17
+   |
+LL |     for<'b> [(); {
+   |         -- lifetime defined here
+LL |         let x: &'b ();
+   |                 ^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/const-generics/late-bound-vars/simple.rs b/tests/ui/const-generics/late-bound-vars/simple.rs
index 544073b5a56..a562bd8cb41 100644
--- a/tests/ui/const-generics/late-bound-vars/simple.rs
+++ b/tests/ui/const-generics/late-bound-vars/simple.rs
@@ -1,22 +1,13 @@
-// failure-status: 101
 // known-bug: unknown
-// error-pattern:internal compiler error
-// normalize-stderr-test "internal compiler error.*" -> ""
-// normalize-stderr-test "DefId\([^)]*\)" -> "..."
-// normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> ""
-// normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> ""
-// normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> ""
-// normalize-stderr-test "note: compiler flags.*\n\n" -> ""
-// normalize-stderr-test "note: rustc.*running on.*\n\n" -> ""
-// normalize-stderr-test "thread.*panicked.*:\n.*\n" -> ""
-// normalize-stderr-test "stack backtrace:\n" -> ""
-// normalize-stderr-test "\s\d{1,}: .*\n" -> ""
-// normalize-stderr-test "\s at .*\n" -> ""
-// normalize-stderr-test ".*note: Some details.*\n" -> ""
-// normalize-stderr-test "\n\n[ ]*\n" -> ""
-// normalize-stderr-test "compiler/.*: projection" -> "projection"
-// normalize-stderr-test ".*omitted \d{1,} frame.*\n" -> ""
-// normalize-stderr-test "error: [\s\n]*query stack" -> "error: query stack"
+
+// If we want this to compile, then we'd need to do something like RPITs do,
+// where nested associated constants have early-bound versions of their captured
+// late-bound vars inserted into their generics. This gives us substitutable
+// lifetimes to actually use when borrow-checking the associated const, which is
+// lowered as a totally separate body from its parent. Since this doesn't exist,
+// we should just error rather than resolving this late-bound var with no
+// binder to actually attach it to, or worse, as a free region that can't even be
+// substituted correctly, and ICEing. - @compiler-errors
 
 #![feature(generic_const_exprs)]
 #![allow(incomplete_features)]
diff --git a/tests/ui/const-generics/late-bound-vars/simple.stderr b/tests/ui/const-generics/late-bound-vars/simple.stderr
index c9f2164b635..e72e373630d 100644
--- a/tests/ui/const-generics/late-bound-vars/simple.stderr
+++ b/tests/ui/const-generics/late-bound-vars/simple.stderr
@@ -1,12 +1,19 @@
-error: query stack during panic:
-#0 [mir_borrowck] borrow-checking `test::{constant#1}`
-#1 [mir_drops_elaborated_and_const_checked] elaborating drops for `test::{constant#1}`
-#2 [mir_for_ctfe] caching mir of `test::{constant#1}` for CTFE
-#3 [eval_to_allocation_raw] const-evaluating + checking `test::{constant#1}`
-#4 [eval_to_allocation_raw] const-evaluating + checking `test::{constant#1}`
-#5 [eval_to_valtree] evaluating type-level constant
-#6 [typeck] type-checking `test`
-#7 [analysis] running analysis passes on this crate
-end of query stack
-error: aborting due to previous error
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/simple.rs:20:25
+   |
+LL | fn test<'a>() {
+   |         -- lifetime defined here
+LL |     let _: [u8; inner::<'a>()];
+   |                         ^^
+
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/simple.rs:21:25
+   |
+LL | fn test<'a>() {
+   |         -- lifetime defined here
+LL |     let _: [u8; inner::<'a>()];
+LL |     let _ = [0; inner::<'a>()];
+   |                         ^^
+
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/const_prop/dont-propagate-generic-instance-2.rs b/tests/ui/const_prop/dont-propagate-generic-instance-2.rs
new file mode 100644
index 00000000000..e5525af23f0
--- /dev/null
+++ b/tests/ui/const_prop/dont-propagate-generic-instance-2.rs
@@ -0,0 +1,26 @@
+// run-pass
+
+#![feature(inline_const)]
+
+// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
+// This is relevant when we have an overlapping impl and builtin dyn instance.
+// See <https://github.com/rust-lang/rust/pull/114941> for more context.
+
+trait Trait {
+    fn foo(&self) -> &'static str;
+}
+
+impl<T: ?Sized> Trait for T {
+    fn foo(&self) -> &'static str {
+        std::any::type_name::<T>()
+    }
+}
+
+fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
+    const { Trait::foo as fn(&T) -> &'static str }
+    // If const prop were to propagate the instance
+}
+
+fn main() {
+    assert_eq!("i32", bar::<dyn Trait>()(&1i32));
+}
diff --git a/tests/ui/const_prop/dont-propagate-generic-instance.rs b/tests/ui/const_prop/dont-propagate-generic-instance.rs
new file mode 100644
index 00000000000..5994961b887
--- /dev/null
+++ b/tests/ui/const_prop/dont-propagate-generic-instance.rs
@@ -0,0 +1,24 @@
+// run-pass
+
+// Makes sure we don't propagate generic instances of `Self: ?Sized` blanket impls.
+// This is relevant when we have an overlapping impl and builtin dyn instance.
+// See <https://github.com/rust-lang/rust/pull/114941> for more context.
+
+trait Trait {
+    fn foo(&self) -> &'static str;
+}
+
+impl<T: ?Sized> Trait for T {
+    fn foo(&self) -> &'static str {
+        std::any::type_name::<T>()
+    }
+}
+
+const fn bar<T: ?Sized>() -> fn(&T) -> &'static str {
+    Trait::foo
+    // If const prop were to propagate the instance
+}
+
+fn main() {
+    assert_eq!("i32", bar::<dyn Trait>()(&1i32));
+}
diff --git a/tests/ui/consts/escaping-bound-var.rs b/tests/ui/consts/escaping-bound-var.rs
new file mode 100644
index 00000000000..7c1fbd24f55
--- /dev/null
+++ b/tests/ui/consts/escaping-bound-var.rs
@@ -0,0 +1,13 @@
+#![feature(generic_const_exprs)]
+//~^ WARN the feature `generic_const_exprs` is incomplete
+
+fn test<'a>(
+    _: &'a (),
+) -> [(); {
+    let x: &'a ();
+    //~^ ERROR cannot capture late-bound lifetime in constant
+    1
+}] {
+}
+
+fn main() {}
diff --git a/tests/ui/consts/escaping-bound-var.stderr b/tests/ui/consts/escaping-bound-var.stderr
new file mode 100644
index 00000000000..d26ae2cee1c
--- /dev/null
+++ b/tests/ui/consts/escaping-bound-var.stderr
@@ -0,0 +1,20 @@
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/escaping-bound-var.rs:1:12
+   |
+LL | #![feature(generic_const_exprs)]
+   |            ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error: cannot capture late-bound lifetime in constant
+  --> $DIR/escaping-bound-var.rs:7:13
+   |
+LL | fn test<'a>(
+   |         -- lifetime defined here
+...
+LL |     let x: &'a ();
+   |             ^^
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/tests/ui/drop/dynamic-drop.rs b/tests/ui/drop/dynamic-drop.rs
index 9e51d3adaaa..caef6358ea7 100644
--- a/tests/ui/drop/dynamic-drop.rs
+++ b/tests/ui/drop/dynamic-drop.rs
@@ -2,6 +2,7 @@
 // needs-unwind
 
 #![feature(generators, generator_trait)]
+#![feature(if_let_guard)]
 
 #![allow(unused_assignments)]
 #![allow(unused_variables)]
@@ -332,6 +333,16 @@ fn move_ref_pattern(a: &Allocator) {
     let (ref _a, ref mut _b, _c, mut _d) = tup;
 }
 
+fn if_let_guard(a: &Allocator, c: bool, d: i32) {
+    let foo = if c { Some(a.alloc()) } else { None };
+
+    match d == 0 {
+        false if let Some(a) = foo => { let b = a; }
+        true if let true = { drop(foo.unwrap_or_else(|| a.alloc())); d == 1 } => {}
+        _ => {}
+    }
+}
+
 fn panic_after_return(a: &Allocator) -> Ptr<'_> {
     // Panic in the drop of `p` or `q` can leak
     let exceptions = vec![8, 9];
@@ -497,6 +508,13 @@ fn main() {
 
     run_test(|a| move_ref_pattern(a));
 
+    run_test(|a| if_let_guard(a, true, 0));
+    run_test(|a| if_let_guard(a, true, 1));
+    run_test(|a| if_let_guard(a, true, 2));
+    run_test(|a| if_let_guard(a, false, 0));
+    run_test(|a| if_let_guard(a, false, 1));
+    run_test(|a| if_let_guard(a, false, 2));
+
     run_test(|a| {
         panic_after_return(a);
     });
diff --git a/tests/ui/generic-associated-types/bugs/issue-88460.stderr b/tests/ui/generic-associated-types/bugs/issue-88460.stderr
index a2047f103d4..b9e2c4186c1 100644
--- a/tests/ui/generic-associated-types/bugs/issue-88460.stderr
+++ b/tests/ui/generic-associated-types/bugs/issue-88460.stderr
@@ -7,6 +7,14 @@ LL |     test(Foo);
    |     required by a bound introduced by this call
    |
    = help: the trait `Marker` is implemented for `()`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/issue-88460.rs:28:10
+   |
+LL |     test(Foo);
+   |     -----^^^-
+   |     |    |
+   |     |    the trait solver is unable to infer the generic types that should be inferred from this argument
+   |     add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `test`
   --> $DIR/issue-88460.rs:15:27
    |
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
index b30dd36d2ad..2cc2bb2bbc3 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr
@@ -8,6 +8,14 @@ LL |         call(f, ());
    |
    = note: expected a closure with arguments `((),)`
               found a closure with arguments `(<_ as ATC<'a>>::Type,)`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/issue-62529-3.rs:25:14
+   |
+LL |         call(f, ());
+   |         -----^-----
+   |         |    |
+   |         |    the trait solver is unable to infer the generic types that should be inferred from this argument
+   |         add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `call`
   --> $DIR/issue-62529-3.rs:9:36
    |
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
index 5be33bccdc3..55eaef78634 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90950.stderr
@@ -7,6 +7,14 @@ LL |     upcast(y)
    |     required by a bound introduced by this call
    |
    = help: the trait `IsCovariant<'a>` is implemented for `std::borrow::Cow<'a, T>`
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/issue-90950.rs:50:12
+   |
+LL |     upcast(y)
+   |     -------^-
+   |     |      |
+   |     |      the trait solver is unable to infer the generic types that should be inferred from this argument
+   |     add turbofish arguments to this call to specify the types manually, even if it's redundant
 note: required by a bound in `upcast`
   --> $DIR/issue-90950.rs:27:42
    |
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
index 73388a72574..081dbb67df8 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/norm-before-method-resolution.stderr
@@ -4,6 +4,11 @@ error[E0277]: the trait bound `for<'a> <_ as Trait<'a>>::Out: Copy` is not satis
 LL |     let _: () = weird_bound();
    |                 ^^^^^^^^^^^ the trait `for<'a> Copy` is not implemented for `<_ as Trait<'a>>::Out`
    |
+note: this is a known limitation of the trait solver that will be lifted in the future
+  --> $DIR/norm-before-method-resolution.rs:22:17
+   |
+LL |     let _: () = weird_bound();
+   |                 ^^^^^^^^^^^ try adding turbofish arguments to this expression to specify the types manually, even if it's redundant
 note: required by a bound in `weird_bound`
   --> $DIR/norm-before-method-resolution.rs:18:40
    |
diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.rs b/tests/ui/impl-trait/coherence-treats-tait-ambig.rs
index 156a7eb0e23..df47208bf36 100644
--- a/tests/ui/impl-trait/coherence-treats-tait-ambig.rs
+++ b/tests/ui/impl-trait/coherence-treats-tait-ambig.rs
@@ -1,6 +1,3 @@
-// revisions: current next
-//[next] compile-flags: -Ztrait-solver=next
-
 #![feature(type_alias_impl_trait)]
 
 type T = impl Sized;
diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.next.stderr b/tests/ui/impl-trait/coherence-treats-tait-ambig.stderr
index 61fed16294b..7c69c4bfe97 100644
--- a/tests/ui/impl-trait/coherence-treats-tait-ambig.next.stderr
+++ b/tests/ui/impl-trait/coherence-treats-tait-ambig.stderr
@@ -1,5 +1,5 @@
 error[E0119]: conflicting implementations of trait `Into<T>` for type `Foo`
-  --> $DIR/coherence-treats-tait-ambig.rs:10:1
+  --> $DIR/coherence-treats-tait-ambig.rs:7:1
    |
 LL | impl Into<T> for Foo {
    | ^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/match/issue-115681.rs b/tests/ui/match/issue-115681.rs
new file mode 100644
index 00000000000..c41e808e170
--- /dev/null
+++ b/tests/ui/match/issue-115681.rs
@@ -0,0 +1,32 @@
+// run-pass
+// compile-flags: -C opt-level=1
+
+// Make sure LLVM does not miscompile this match.
+fn main() {
+    enum Bits {
+        None = 0x00,
+        Low = 0x40,
+        High = 0x80,
+        Both = 0xC0,
+    }
+
+    let value = Box::new(0x40u8);
+    let mut out = Box::new(0u8);
+
+    let bits = match *value {
+        0x00 => Bits::None,
+        0x40 => Bits::Low,
+        0x80 => Bits::High,
+        0xC0 => Bits::Both,
+        _ => return,
+    };
+
+    match bits {
+        Bits::None | Bits::Low => {
+            *out = 1;
+        }
+        _ => (),
+    }
+
+    assert_eq!(*out, 1);
+}
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/const-expr.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/const-expr.rs
new file mode 100644
index 00000000000..5c42c0d8bec
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/const-expr.rs
@@ -0,0 +1,26 @@
+// Ensure if let guards can be used in constant expressions.
+// build-pass
+
+#![feature(if_let_guard)]
+
+const fn match_if_let(x: Option<i32>, y: Option<i32>) -> i32 {
+    match x {
+        None if let Some(a @ 5) = y => a,
+        Some(z) if let (Some(_), 12) = (y, z) => 2,
+        _ => 3,
+    }
+}
+
+const ASSERTS: usize = {
+    assert!(match_if_let(None, Some(5)) == 5);
+    assert!(match_if_let(Some(12), Some(3)) == 2);
+    assert!(match_if_let(None, Some(4)) == 3);
+    assert!(match_if_let(Some(11), Some(3)) == 3);
+    assert!(match_if_let(Some(12), None) == 3);
+    assert!(match_if_let(None, None) == 3);
+    0
+};
+
+fn main() {
+    let _: [(); ASSERTS];
+}
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs
new file mode 100644
index 00000000000..5c333cd7795
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs
@@ -0,0 +1,97 @@
+#![feature(if_let_guard)]
+#![feature(let_chains)]
+#![allow(irrefutable_let_patterns)]
+
+fn same_pattern(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, 2) if let y = x && c => (),
+        (1, 2) if let z = x => (), //~ ERROR use of moved value: `x`
+        _ => (),
+    }
+}
+
+fn same_pattern_ok(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, 2) if c && let y = x => (),
+        (1, 2) if let z = x => (),
+        _ => (),
+    }
+}
+
+fn different_patterns(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, _) if let y = x && c => (),
+        (_, 2) if let z = x => (), //~ ERROR use of moved value: `x`
+        _ => (),
+    }
+}
+
+fn different_patterns_ok(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, _) if c && let y = x => (),
+        (_, 2) if let z = x => (),
+        _ => (),
+    }
+}
+
+fn or_pattern(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, _) | (_, 2) if let y = x && c => (), //~ ERROR use of moved value: `x`
+        _ => (),
+    }
+}
+
+fn or_pattern_ok(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, _) | (_, 2) if c && let y = x => (),
+        _ => (),
+    }
+}
+
+fn use_in_arm(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, 2) if let y = x && c => false,
+        _ => { *x == 1 }, //~ ERROR use of moved value: `x`
+    };
+}
+
+fn use_in_arm_ok(c: bool) {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, 2) if c && let y = x => false,
+        _ => { *x == 1 },
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.stderr
new file mode 100644
index 00000000000..d27fde58244
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.stderr
@@ -0,0 +1,67 @@
+error[E0382]: use of moved value: `x`
+  --> $DIR/move-guard-if-let-chain.rs:12:27
+   |
+LL |     let x: Box<_> = Box::new(1);
+   |         - move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+...
+LL |         (1, 2) if let y = x && c => (),
+   |                       - value moved here
+LL |         (1, 2) if let z = x => (),
+   |                           ^ value used here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         (1, 2) if let ref y = x && c => (),
+   |                       +++
+
+error[E0382]: use of moved value: `x`
+  --> $DIR/move-guard-if-let-chain.rs:36:27
+   |
+LL |     let x: Box<_> = Box::new(1);
+   |         - move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+...
+LL |         (1, _) if let y = x && c => (),
+   |                       - value moved here
+LL |         (_, 2) if let z = x => (),
+   |                           ^ value used here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         (1, _) if let ref y = x && c => (),
+   |                       +++
+
+error[E0382]: use of moved value: `x`
+  --> $DIR/move-guard-if-let-chain.rs:59:36
+   |
+LL |     let x: Box<_> = Box::new(1);
+   |         - move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+...
+LL |         (1, _) | (_, 2) if let y = x && c => (),
+   |                                -   ^ value used here after move
+   |                                |
+   |                                value moved here
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         (1, _) | (_, 2) if let ref y = x && c => (),
+   |                                +++
+
+error[E0382]: use of moved value: `x`
+  --> $DIR/move-guard-if-let-chain.rs:82:16
+   |
+LL |     let x: Box<_> = Box::new(1);
+   |         - move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+...
+LL |         (1, 2) if let y = x && c => false,
+   |                       - value moved here
+LL |         _ => { *x == 1 },
+   |                ^^ value used here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         (1, 2) if let ref y = x && c => false,
+   |                       +++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let.rs
new file mode 100644
index 00000000000..071b86e2e14
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let.rs
@@ -0,0 +1,41 @@
+// Check that borrowck knows that moves in the pattern for if-let guards
+// only happen when the pattern is matched.
+
+// build-pass
+
+#![feature(if_let_guard)]
+#![allow(irrefutable_let_patterns)]
+
+fn same_pattern() {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, 2) if let y = x => (),
+        (1, 2) if let z = x => (),
+        _ => (),
+    }
+}
+
+fn or_pattern() {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, _) | (_, 2) if let y = x => (),
+        _ => (),
+    }
+}
+
+fn main() {
+    let x: Box<_> = Box::new(1);
+
+    let v = (1, 2);
+
+    match v {
+        (1, 2) if let y = x => false,
+        _ => { *x == 1 },
+    };
+}
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/type-inference.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/type-inference.rs
new file mode 100644
index 00000000000..ef7a772e6c5
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/type-inference.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+#![feature(if_let_guard)]
+
+struct S;
+
+fn get<T>() -> Option<T> {
+    None
+}
+
+fn main() {
+    match get() {
+        x if let Some(S) = x => {}
+        _ => {}
+    }
+}
diff --git a/tests/ui/sanitize/cfg.rs b/tests/ui/sanitize/cfg.rs
index 523de1ceaee..761c646ec88 100644
--- a/tests/ui/sanitize/cfg.rs
+++ b/tests/ui/sanitize/cfg.rs
@@ -1,15 +1,16 @@
 // Verifies that when compiling with -Zsanitizer=option,
 // the `#[cfg(sanitize = "option")]` attribute is configured.
 
-// needs-sanitizer-support
 // check-pass
 // revisions: address cfi kcfi leak memory thread
+//compile-flags: -Ctarget-feature=-crt-static
 //[address]needs-sanitizer-address
 //[address]compile-flags: -Zsanitizer=address --cfg address
 //[cfi]needs-sanitizer-cfi
-//[cfi]compile-flags:     -Zsanitizer=cfi     --cfg cfi -Clto
-//[kcfi]needs-sanitizer-kcfi
-//[kcfi]compile-flags:    -Zsanitizer=kcfi    --cfg kcfi
+//[cfi]compile-flags:     -Zsanitizer=cfi     --cfg cfi
+//[cfi]compile-flags:     -Clto -Ccodegen-units=1
+//[kcfi]needs-llvm-components: x86
+//[kcfi]compile-flags:    -Zsanitizer=kcfi    --cfg kcfi --target x86_64-unknown-none
 //[leak]needs-sanitizer-leak
 //[leak]compile-flags:    -Zsanitizer=leak    --cfg leak
 //[memory]needs-sanitizer-memory
@@ -17,7 +18,14 @@
 //[thread]needs-sanitizer-thread
 //[thread]compile-flags:  -Zsanitizer=thread  --cfg thread
 
-#![feature(cfg_sanitize)]
+#![feature(cfg_sanitize, no_core, lang_items)]
+#![crate_type="lib"]
+#![no_core]
+
+#[lang="sized"]
+trait Sized { }
+#[lang="copy"]
+trait Copy { }
 
 #[cfg(all(sanitize = "address", address))]
 fn main() {}
@@ -36,3 +44,7 @@ fn main() {}
 
 #[cfg(all(sanitize = "thread", thread))]
 fn main() {}
+
+pub fn check() {
+    main();
+}
diff --git a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr
index 8eda64e4490..46677a58316 100644
--- a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr
+++ b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr
@@ -5,6 +5,8 @@ LL | impl<T: TraitB> Overlaps<Box<T>> for <T as TraitB>::Assoc {}
    | --------------------------------------------------------- first implementation here
 LL | impl<U: TraitB> Overlaps<U> for <U as TraitB>::Assoc {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `<_ as TraitB>::Assoc`
+   |
+   = note: downstream crates may implement trait `TraitB` for type `std::boxed::Box<_>`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr b/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr
index 5d5f325e4b4..73d46c4df96 100644
--- a/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr
+++ b/tests/ui/traits/new-solver/coherence/trait_ref_is_knowable-norm-overflow.stderr
@@ -1,3 +1,7 @@
+WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized>
+WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized>
+WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized>
+WARN rustc_trait_selection::traits::coherence expected an unknowable trait ref: <<LocalTy as Overflow>::Assoc as std::marker::Sized>
 error[E0119]: conflicting implementations of trait `Trait` for type `<LocalTy as Overflow>::Assoc`
   --> $DIR/trait_ref_is_knowable-norm-overflow.rs:17:1
    |
@@ -6,6 +10,8 @@ LL | impl<T: Copy> Trait for T {}
 LL | struct LocalTy;
 LL | impl Trait for <LocalTy as Overflow>::Assoc {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `<LocalTy as Overflow>::Assoc`
+   |
+   = note: upstream crates may add a new impl of trait `std::marker::Copy` for type `<LocalTy as Overflow>::Assoc` in future versions
 
 error: aborting due to previous error
 
diff --git a/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.rs b/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.rs
index 91c6dfb8e0a..bbae67f0bad 100644
--- a/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.rs
+++ b/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.rs
@@ -4,7 +4,7 @@
 fn b()
 where
     for<const C: usize> [(); C]: Copy,
-    //~^ ERROR cannot capture late-bound const parameter in a constant
+    //~^ ERROR cannot capture late-bound const parameter in constant
 {
 }
 
diff --git a/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.stderr b/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.stderr
index 69bb605bf41..d65892ec6df 100644
--- a/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.stderr
+++ b/tests/ui/traits/non_lifetime_binders/capture-late-ct-in-anon.stderr
@@ -7,7 +7,7 @@ LL | #![feature(non_lifetime_binders)]
    = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information
    = note: `#[warn(incomplete_features)]` on by default
 
-error: cannot capture late-bound const parameter in a constant
+error: cannot capture late-bound const parameter in constant
   --> $DIR/capture-late-ct-in-anon.rs:6:30
    |
 LL |     for<const C: usize> [(); C]: Copy,
diff --git a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs
index 3903bfe9bcf..64f09f823fc 100644
--- a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs
+++ b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs
@@ -5,7 +5,7 @@
 fn foo() -> usize
 where
     for<T> [i32; { let _: T = todo!(); 0 }]:,
-    //~^ ERROR cannot capture late-bound type parameter in a constant
+    //~^ ERROR cannot capture late-bound type parameter in constant
 {}
 
 fn main() {}
diff --git a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr
index fafff02dea6..dc54e1acc39 100644
--- a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr
+++ b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr
@@ -15,7 +15,7 @@ LL | #![feature(non_lifetime_binders, generic_const_exprs)]
    |
    = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
 
-error: cannot capture late-bound type parameter in a constant
+error: cannot capture late-bound type parameter in constant
   --> $DIR/late-bound-in-anon-ct.rs:7:27
    |
 LL |     for<T> [i32; { let _: T = todo!(); 0 }]:,
diff --git a/tests/ui/type-alias-impl-trait/escaping-bound-var.rs b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs
new file mode 100644
index 00000000000..bf27e76db2b
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/escaping-bound-var.rs
@@ -0,0 +1,22 @@
+#![feature(type_alias_impl_trait)]
+
+pub trait Trait<'a> {
+    type Assoc;
+}
+
+trait Test<'a> {}
+
+pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
+//~^ ERROR cannot capture late-bound lifetime in type alias impl trait
+
+impl Trait<'_> for () {
+    type Assoc = ();
+}
+
+impl Test<'_> for () {}
+
+fn constrain() -> Foo {
+    ()
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr b/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr
new file mode 100644
index 00000000000..8205a60ccdd
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/escaping-bound-var.stderr
@@ -0,0 +1,8 @@
+error: cannot capture late-bound lifetime in type alias impl trait
+  --> $DIR/escaping-bound-var.rs:9:57
+   |
+LL | pub type Foo = impl for<'a> Trait<'a, Assoc = impl Test<'a>>;
+   |                         -- lifetime defined here        ^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
new file mode 100644
index 00000000000..baa22e1ce18
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
@@ -0,0 +1,29 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<() as Recur>::Recur == _`
+
+#![feature(impl_trait_in_assoc_type)]
+
+use core::future::Future;
+
+trait Recur {
+    type Recur: Future<Output = ()>;
+
+    fn recur(self) -> Self::Recur;
+}
+
+async fn recur(t: impl Recur) {
+    t.recur().await;
+}
+
+impl Recur for () {
+    type Recur = impl Future<Output = ()>;
+
+    fn recur(self) -> Self::Recur {
+        async move { recur(self).await; }
+    }
+}
+
+fn main() {
+    recur(());
+}
diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
new file mode 100644
index 00000000000..0238694c24d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<() as Recur>::Recur == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs
new file mode 100644
index 00000000000..1ccd1b0cbad
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs
@@ -0,0 +1,40 @@
+// edition: 2021
+// build-fail
+//~^^ ERROR overflow evaluating the requirement `<() as B>::Assoc == _`
+
+#![feature(rustc_attrs)]
+#![feature(impl_trait_in_assoc_type)]
+
+#[rustc_coinductive]
+trait A {
+    type Assoc;
+
+    fn test() -> Self::Assoc;
+}
+
+#[rustc_coinductive]
+trait B {
+    type Assoc;
+
+    fn test() -> Self::Assoc;
+}
+
+impl<T: A> B for T {
+    type Assoc = impl Sized;
+
+    fn test() -> <Self as B>::Assoc {
+        <T as A>::test()
+    }
+}
+
+fn main() {
+    <() as A>::test();
+}
+
+impl<T: B> A for T {
+    type Assoc = impl Sized;
+
+    fn test() -> <Self as A>::Assoc {
+        <T as B>::test()
+    }
+}
diff --git a/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr
new file mode 100644
index 00000000000..49c59f7eb37
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/mututally-recursive-overflow.stderr
@@ -0,0 +1,5 @@
+error[E0275]: overflow evaluating the requirement `<() as B>::Assoc == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/type-alias-impl-trait/variance.rs b/tests/ui/type-alias-impl-trait/variance.rs
index eba4816003b..e92cf2513e7 100644
--- a/tests/ui/type-alias-impl-trait/variance.rs
+++ b/tests/ui/type-alias-impl-trait/variance.rs
@@ -9,42 +9,36 @@ type NotCapturedEarly<'a> = impl Sized; //~ [o]
 
 type CapturedEarly<'a> = impl Sized + Captures<'a>; //~ [o]
 
+// TAIT does *not* capture `'b`
 type NotCapturedLate<'a> = dyn for<'b> Iterator<Item = impl Sized>; //~ [o]
 
-type CapturedLate<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'b>>; //~ [o]
-
-type Captured<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'a> + Captures<'b>>; //~ [o]
+// TAIT does *not* capture `'b`
+type Captured<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'a>>; //~ [o]
 
 type Bar<'a, 'b: 'b, T> = impl Sized; //~ ERROR [o, o, o]
 
 trait Foo<'i> {
-    type ImplicitCapturedEarly<'a>;
-
-    type ExplicitCaptureEarly<'a>;
+    type ImplicitCapture<'a>;
 
-    type ImplicitCaptureLate<'a>;
+    type ExplicitCaptureFromHeader<'a>;
 
-    type ExplicitCaptureLate<'a>;
+    type ExplicitCaptureFromGat<'a>;
 }
 
 impl<'i> Foo<'i> for &'i () {
-    type ImplicitCapturedEarly<'a> = impl Sized; //~ [o, o]
+    type ImplicitCapture<'a> = impl Sized; //~ [o, o]
 
-    type ExplicitCaptureEarly<'a> = impl Sized + Captures<'i>; //~ [o, o]
+    type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ [o, o]
 
-    type ImplicitCaptureLate<'a> = impl Sized; //~ [o, o]
-
-    type ExplicitCaptureLate<'a> = impl Sized + Captures<'a>; //~ [o, o]
+    type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ [o, o]
 }
 
 impl<'i> Foo<'i> for () {
-    type ImplicitCapturedEarly<'a> = impl Sized; //~ [o, o]
-
-    type ExplicitCaptureEarly<'a> = impl Sized + Captures<'i>; //~ [o, o]
+    type ImplicitCapture<'a> = impl Sized; //~ [o, o]
 
-    type ImplicitCaptureLate<'a> = impl Sized; //~ [o, o]
+    type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>; //~ [o, o]
 
-    type ExplicitCaptureLate<'a> = impl Sized + Captures<'a>; //~ [o, o]
+    type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>; //~ [o, o]
 }
 
 fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/variance.stderr b/tests/ui/type-alias-impl-trait/variance.stderr
index f73cf617a4c..1794447c89a 100644
--- a/tests/ui/type-alias-impl-trait/variance.stderr
+++ b/tests/ui/type-alias-impl-trait/variance.stderr
@@ -11,22 +11,16 @@ LL | type CapturedEarly<'a> = impl Sized + Captures<'a>;
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: [o]
-  --> $DIR/variance.rs:12:56
+  --> $DIR/variance.rs:13:56
    |
 LL | type NotCapturedLate<'a> = dyn for<'b> Iterator<Item = impl Sized>;
    |                                                        ^^^^^^^^^^
 
 error: [o]
-  --> $DIR/variance.rs:14:53
-   |
-LL | type CapturedLate<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'b>>;
-   |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: [o]
   --> $DIR/variance.rs:16:49
    |
-LL | type Captured<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'a> + Captures<'b>>;
-   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | type Captured<'a> = dyn for<'b> Iterator<Item = impl Sized + Captures<'a>>;
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: [o, o, o]
   --> $DIR/variance.rs:18:27
@@ -35,52 +29,40 @@ LL | type Bar<'a, 'b: 'b, T> = impl Sized;
    |                           ^^^^^^^^^^
 
 error: [o, o]
-  --> $DIR/variance.rs:31:38
-   |
-LL |     type ImplicitCapturedEarly<'a> = impl Sized;
-   |                                      ^^^^^^^^^^
-
-error: [o, o]
-  --> $DIR/variance.rs:33:37
-   |
-LL |     type ExplicitCaptureEarly<'a> = impl Sized + Captures<'i>;
-   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: [o, o]
-  --> $DIR/variance.rs:35:36
+  --> $DIR/variance.rs:29:32
    |
-LL |     type ImplicitCaptureLate<'a> = impl Sized;
-   |                                    ^^^^^^^^^^
+LL |     type ImplicitCapture<'a> = impl Sized;
+   |                                ^^^^^^^^^^
 
 error: [o, o]
-  --> $DIR/variance.rs:37:36
+  --> $DIR/variance.rs:31:42
    |
-LL |     type ExplicitCaptureLate<'a> = impl Sized + Captures<'a>;
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>;
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: [o, o]
-  --> $DIR/variance.rs:41:38
+  --> $DIR/variance.rs:33:39
    |
-LL |     type ImplicitCapturedEarly<'a> = impl Sized;
-   |                                      ^^^^^^^^^^
+LL |     type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>;
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: [o, o]
-  --> $DIR/variance.rs:43:37
+  --> $DIR/variance.rs:37:32
    |
-LL |     type ExplicitCaptureEarly<'a> = impl Sized + Captures<'i>;
-   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     type ImplicitCapture<'a> = impl Sized;
+   |                                ^^^^^^^^^^
 
 error: [o, o]
-  --> $DIR/variance.rs:45:36
+  --> $DIR/variance.rs:39:42
    |
-LL |     type ImplicitCaptureLate<'a> = impl Sized;
-   |                                    ^^^^^^^^^^
+LL |     type ExplicitCaptureFromHeader<'a> = impl Sized + Captures<'i>;
+   |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: [o, o]
-  --> $DIR/variance.rs:47:36
+  --> $DIR/variance.rs:41:39
    |
-LL |     type ExplicitCaptureLate<'a> = impl Sized + Captures<'a>;
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     type ExplicitCaptureFromGat<'a> = impl Sized + Captures<'a>;
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 14 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/tests/ui/unreachable-code.rs b/tests/ui/unreachable-code.rs
index 28b938edc63..64174db7afb 100644
--- a/tests/ui/unreachable-code.rs
+++ b/tests/ui/unreachable-code.rs
@@ -2,25 +2,32 @@
 
 #![allow(unused_must_use)]
 #![allow(dead_code)]
-
 #![allow(path_statements)]
 #![allow(unreachable_code)]
 #![allow(unused_variables)]
+#![feature(if_let_guard)]
 
-fn id(x: bool) -> bool { x }
+fn id(x: bool) -> bool {
+    x
+}
 
 fn call_id() {
     let c = panic!();
     id(c);
 }
 
-fn call_id_2() { id(true) && id(return); }
+fn call_id_2() {
+    id(true) && id(return);
+}
 
-fn call_id_3() { id(return) && id(return); }
+fn call_id_3() {
+    id(return) && id(return);
+}
 
 fn ret_guard() {
     match 2 {
       x if (return) => { x; }
+      x if let true = return => { x; }
       _ => {}
     }
 }
diff --git a/triagebot.toml b/triagebot.toml
index 5b4e653b100..4a84f3caa95 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -13,7 +13,7 @@ allow-unauthenticated = [
     "WG-*",
     "beta-nominated",
     "const-hack",
-    "llvm-main",
+    "llvm-*",
     "needs-fcp",
     "relnotes",
     "requires-*",