about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml6
-rw-r--r--Cargo.lock20
-rw-r--r--compiler/rustc_ast/src/ast.rs11
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs35
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs12
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs9
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs11
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs5
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs4
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs16
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl3
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs13
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs1
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs2
-rw-r--r--compiler/rustc_expand/src/build.rs20
-rw-r--r--compiler/rustc_expand/src/expand.rs9
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs63
-rw-r--r--compiler/rustc_feature/src/lib.rs4
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs25
-rw-r--r--compiler/rustc_hir/src/intravisit.rs13
-rw-r--r--compiler/rustc_hir/src/stable_hash_impls.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs8
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs26
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs13
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/infer/at.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs13
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs7
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs7
-rw-r--r--compiler/rustc_infer/src/infer/outlives/test_type_match.rs17
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/leak_check.rs4
-rw-r--r--compiler/rustc_infer/src/infer/relate/_match.rs (renamed from compiler/rustc_middle/src/ty/_match.rs)22
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs17
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs15
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs10
-rw-r--r--compiler/rustc_infer/src/infer/relate/higher_ranked.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lattice.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs10
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs20
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs17
-rw-r--r--compiler/rustc_lint/src/builtin.rs21
-rw-r--r--compiler/rustc_metadata/src/creader.rs1
-rw-r--r--compiler/rustc_metadata/src/locator.rs34
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/arena.rs2
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs42
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs5
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs51
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/query.rs9
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs4
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs6
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs8
-rw-r--r--compiler/rustc_middle/src/ty/context.rs39
-rw-r--r--compiler/rustc_middle/src/ty/error.rs163
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs36
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs817
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs8
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs119
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs5
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs7
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs8
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs12
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs34
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs1
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs68
-rw-r--r--compiler/rustc_next_trait_solver/Cargo.toml11
-rw-r--r--compiler/rustc_next_trait_solver/src/resolve.rs8
-rw-r--r--compiler/rustc_parse/messages.ftl2
-rw-r--r--compiler/rustc_parse/src/errors.rs9
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs35
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs26
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs40
-rw-r--r--compiler/rustc_parse/src/parser/item.rs4
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs56
-rw-r--r--compiler/rustc_parse/src/parser/mut_visit/tests.rs18
-rw-r--r--compiler/rustc_parse/src/parser/path.rs2
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs2
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs2
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs35
-rw-r--r--compiler/rustc_passes/messages.ftl4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs21
-rw-r--r--compiler/rustc_passes/src/check_const.rs10
-rw-r--r--compiler/rustc_passes/src/dead.rs142
-rw-r--r--compiler/rustc_passes/src/errors.rs11
-rw-r--r--compiler/rustc_passes/src/liveness.rs8
-rw-r--r--compiler/rustc_passes/src/loops.rs7
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs10
-rw-r--r--compiler/rustc_resolve/src/ident.rs10
-rw-r--r--compiler/rustc_resolve/src/late.rs6
-rw-r--r--compiler/rustc_session/src/filesearch.rs18
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/lib.rs12
-rw-r--r--compiler/rustc_target/src/spec/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs5
-rw-r--r--compiler/rustc_type_ir/src/error.rs106
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs56
-rw-r--r--compiler/rustc_type_ir/src/interner.rs46
-rw-r--r--compiler/rustc_type_ir/src/lib.rs8
-rw-r--r--compiler/rustc_type_ir/src/macros.rs18
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs33
-rw-r--r--compiler/rustc_type_ir/src/relate.rs666
-rw-r--r--compiler/rustc_type_ir/src/solve.rs6
-rw-r--r--config.example.toml4
-rw-r--r--library/alloc/src/collections/binary_heap/mod.rs3
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/tests/lib.rs1
m---------library/backtrace0
-rw-r--r--library/core/src/error.rs16
-rw-r--r--library/core/src/fmt/num.rs33
-rw-r--r--library/core/src/option.rs4
-rw-r--r--library/core/src/pin.rs2
-rw-r--r--library/core/tests/lib.rs1
-rw-r--r--library/std/Cargo.toml4
-rw-r--r--library/std/src/ffi/os_str.rs19
-rw-r--r--library/std/src/ffi/os_str/tests.rs9
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--library/std/src/path.rs19
-rw-r--r--library/std/src/path/tests.rs10
-rw-r--r--library/std/src/sys/os_str/bytes.rs5
-rw-r--r--library/std/src/sys/os_str/wtf8.rs5
-rw-r--r--library/std/src/sys/pal/windows/c/README.md2
-rw-r--r--library/std/src/sys_common/wtf8.rs5
-rw-r--r--src/bootstrap/mk/Makefile.in7
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs4
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile3
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh101
-rw-r--r--src/doc/rustc/src/command-line-arguments.md40
-rw-r--r--src/librustdoc/clean/cfg/tests.rs6
-rw-r--r--src/librustdoc/doctest.rs1035
-rw-r--r--src/librustdoc/doctest/make.rs393
-rw-r--r--src/librustdoc/doctest/markdown.rs125
-rw-r--r--src/librustdoc/doctest/rust.rs198
-rw-r--r--src/librustdoc/doctest/tests.rs59
-rw-r--r--src/librustdoc/html/markdown.rs44
-rw-r--r--src/librustdoc/html/static/.eslintrc.js2
-rw-r--r--src/librustdoc/html/static/js/externs.js3
-rw-r--r--src/librustdoc/html/static/js/search.js156
-rw-r--r--src/librustdoc/lib.rs2
-rw-r--r--src/librustdoc/markdown.rs48
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs6
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs6
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.stderr32
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed6
-rw-r--r--src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs6
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.fixed4
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs2
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.stderr8
-rw-r--r--src/tools/compiletest/src/runtest.rs40
-rw-r--r--src/tools/compiletest/src/util.rs16
-rw-r--r--src/tools/run-make-support/src/cc.rs13
-rw-r--r--src/tools/run-make-support/src/clang.rs8
-rw-r--r--src/tools/run-make-support/src/lib.rs56
-rw-r--r--src/tools/run-make-support/src/run.rs6
-rw-r--r--src/tools/run-make-support/src/rustc.rs4
-rw-r--r--src/tools/rust-installer/src/compression.rs12
-rw-r--r--src/tools/rustdoc-gui/.eslintrc.js2
-rw-r--r--src/tools/rustdoc-js/.eslintrc.js2
-rw-r--r--src/tools/rustfmt/tests/source/attrib.rs4
-rw-r--r--src/tools/rustfmt/tests/target/attrib.rs4
-rw-r--r--src/version2
-rw-r--r--tests/codegen-units/item-collection/generic-impl.rs10
-rw-r--r--tests/codegen-units/item-collection/overloaded-operators.rs24
-rw-r--r--tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs6
-rw-r--r--tests/mir-opt/building/custom/consts.consts.built.after.mir2
-rw-r--r--tests/pretty/ast-stmt-expr-attr.rs18
-rw-r--r--tests/pretty/stmt_expr_attributes.rs19
-rw-r--r--tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs14
-rw-r--r--tests/run-make/arguments-non-c-like-enum/rmake.rs4
-rw-r--r--tests/run-make/artifact-incr-cache-no-obj/rmake.rs6
-rw-r--r--tests/run-make/artifact-incr-cache/rmake.rs6
-rw-r--r--tests/run-make/bare-outfile/rmake.rs6
-rw-r--r--tests/run-make/box-struct-no-segfault/rmake.rs4
-rw-r--r--tests/run-make/c-link-to-rust-dylib/rmake.rs14
-rw-r--r--tests/run-make/c-link-to-rust-staticlib/rmake.rs6
-rw-r--r--tests/run-make/c-link-to-rust-va-list-fn/rmake.rs4
-rw-r--r--tests/run-make/cdylib/rmake.rs12
-rw-r--r--tests/run-make/compiler-builtins/Cargo.toml7
-rw-r--r--tests/run-make/compiler-builtins/lib.rs1
-rw-r--r--tests/run-make/compiler-builtins/rmake.rs18
-rw-r--r--tests/run-make/const-prop-lint/rmake.rs4
-rw-r--r--tests/run-make/core-no-oom-handling/rmake.rs4
-rw-r--r--tests/run-make/cross-lang-lto-riscv-abi/rmake.rs6
-rw-r--r--tests/run-make/deref-impl-rustdoc-ice/rmake.rs4
-rw-r--r--tests/run-make/doctests-keep-binaries/rmake.rs14
-rw-r--r--tests/run-make/doctests-runtool/rmake.rs8
-rw-r--r--tests/run-make/emit-named-files/rmake.rs4
-rw-r--r--tests/run-make/exit-code/rmake.rs4
-rw-r--r--tests/run-make/external-crate-panic-handle-no-lint/rmake.rs4
-rw-r--r--tests/run-make/incr-prev-body-beyond-eof/rmake.rs14
-rw-r--r--tests/run-make/issue-107495-archive-permissions/rmake.rs4
-rw-r--r--tests/run-make/issue-125484-used-dependencies/rmake.rs4
-rw-r--r--tests/run-make/manual-crate-name/rmake.rs5
-rw-r--r--tests/run-make/mixing-formats/rmake.rs27
-rw-r--r--tests/run-make/no-intermediate-extras/rmake.rs4
-rw-r--r--tests/run-make/non-pie-thread-local/rmake.rs6
-rw-r--r--tests/run-make/non-unicode-in-incremental-dir/rmake.rs8
-rw-r--r--tests/run-make/notify-all-emit-artifacts/rmake.rs8
-rw-r--r--tests/run-make/panic-impl-transitive/rmake.rs9
-rw-r--r--tests/run-make/print-cfg/rmake.rs6
-rw-r--r--tests/run-make/print-to-output/rmake.rs5
-rw-r--r--tests/run-make/reachable-extern-fn-available-lto/rmake.rs4
-rw-r--r--tests/run-make/repr128-dwarf/rmake.rs8
-rw-r--r--tests/run-make/reset-codegen-1/rmake.rs4
-rw-r--r--tests/run-make/resolve-rename/rmake.rs5
-rw-r--r--tests/run-make/rustdoc-determinism/rmake.rs7
-rw-r--r--tests/run-make/rustdoc-map-file/rmake.rs4
-rw-r--r--tests/run-make/rustdoc-output-path/rmake.rs5
-rw-r--r--tests/run-make/rustdoc-scrape-examples-macros/rmake.rs11
-rw-r--r--tests/run-make/rustdoc-scrape-examples-remap/scrape.rs7
-rw-r--r--tests/run-make/rustdoc-target-spec-json-path/rmake.rs6
-rw-r--r--tests/run-make/rustdoc-test-args/rmake.rs6
-rw-r--r--tests/run-make/rustdoc-themes/rmake.rs7
-rw-r--r--tests/run-make/rustdoc-verify-output-files/rmake.rs6
-rw-r--r--tests/run-make/rustdoc-with-out-dir-option/rmake.rs4
-rw-r--r--tests/run-make/rustdoc-with-output-option/rmake.rs4
-rw-r--r--tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs4
-rw-r--r--tests/run-make/same-lib-two-locations-no-panic/bar.rs3
-rw-r--r--tests/run-make/same-lib-two-locations-no-panic/foo.rs1
-rw-r--r--tests/run-make/same-lib-two-locations-no-panic/rmake.rs28
-rw-r--r--tests/run-make/stdin-rustc/rmake.rs9
-rw-r--r--tests/run-make/stdin-rustdoc/rmake.rs6
-rw-r--r--tests/run-make/suspicious-library/rmake.rs6
-rw-r--r--tests/run-make/wasm-abi/rmake.rs4
-rw-r--r--tests/run-make/wasm-custom-section/rmake.rs4
-rw-r--r--tests/run-make/wasm-custom-sections-opt/rmake.rs4
-rw-r--r--tests/run-make/wasm-export-all-symbols/rmake.rs9
-rw-r--r--tests/run-make/wasm-import-module/rmake.rs4
-rw-r--r--tests/run-make/wasm-panic-small/rmake.rs4
-rw-r--r--tests/run-make/wasm-spurious-import/rmake.rs4
-rw-r--r--tests/run-make/wasm-stringify-ints-small/rmake.rs4
-rw-r--r--tests/run-make/wasm-symbols-different-module/rmake.rs5
-rw-r--r--tests/run-make/wasm-symbols-not-exported/rmake.rs10
-rw-r--r--tests/run-make/wasm-symbols-not-imported/rmake.rs10
-rw-r--r--tests/run-make/windows-binary-no-external-deps/rmake.rs4
-rw-r--r--tests/run-make/windows-spawn/rmake.rs5
-rw-r--r--tests/run-make/windows-ws2_32/rmake.rs5
-rw-r--r--tests/rustdoc-js-std/parser-errors.js23
-rw-r--r--tests/rustdoc-js/basic.js2
-rw-r--r--tests/rustdoc-js/doc-alias.js22
-rw-r--r--tests/rustdoc-js/doc-alias.rs21
-rw-r--r--tests/rustdoc-js/non-english-identifier.js163
-rw-r--r--tests/rustdoc-js/non-english-identifier.rs47
-rw-r--r--tests/ui-fulldeps/stable-mir/check_instance.rs11
-rw-r--r--tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs7
-rw-r--r--tests/ui/attributes/unsafe/derive-unsafe-attributes.rs6
-rw-r--r--tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr8
-rw-r--r--tests/ui/attributes/unsafe/double-unsafe-attributes.rs9
-rw-r--r--tests/ui/attributes/unsafe/double-unsafe-attributes.stderr27
-rw-r--r--tests/ui/attributes/unsafe/unsafe-attributes.rs7
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute.rs6
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr10
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs8
-rw-r--r--tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr10
-rw-r--r--tests/ui/coherence/re-rebalance-coherence.rs1
-rw-r--r--tests/ui/const-generics/defaults/repr-c-issue-82792.rs1
-rw-r--r--tests/ui/const-generics/generic_const_exprs/associated-consts.rs3
-rw-r--r--tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs1
-rw-r--r--tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs4
-rw-r--r--tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr16
-rw-r--r--tests/ui/feature-gates/feature-gate-unsafe-attributes.rs8
-rw-r--r--tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr13
-rw-r--r--tests/ui/imports/cycle-import-in-std-1.rs9
-rw-r--r--tests/ui/imports/cycle-import-in-std-1.stderr13
-rw-r--r--tests/ui/imports/cycle-import-in-std-2.rs9
-rw-r--r--tests/ui/imports/cycle-import-in-std-2.stderr13
-rw-r--r--tests/ui/inline-const/const_block_pat_liveness.rs18
-rw-r--r--tests/ui/issues/issue-5708.rs1
-rw-r--r--tests/ui/lint/dead-code/lint-dead-code-1.rs5
-rw-r--r--tests/ui/lint/dead-code/lint-dead-code-1.stderr26
-rw-r--r--tests/ui/lint/dead-code/unused-assoc-const.rs20
-rw-r--r--tests/ui/lint/dead-code/unused-assoc-const.stderr16
-rw-r--r--tests/ui/lint/dead-code/unused-pub-struct.rs48
-rw-r--r--tests/ui/lint/dead-code/unused-pub-struct.stderr14
-rw-r--r--tests/ui/lint/non-local-defs/consts.stderr19
-rw-r--r--tests/ui/lint/unreachable_pub.rs10
-rw-r--r--tests/ui/lint/unreachable_pub.stderr32
-rw-r--r--tests/ui/lint/unused/unused-doc-comments-edge-cases.rs4
-rw-r--r--tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr14
-rw-r--r--tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed15
-rw-r--r--tests/ui/parser/attribute/attr-binary-expr-ambigous.rs15
-rw-r--r--tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr46
-rw-r--r--tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed3
-rw-r--r--tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs3
-rw-r--r--tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr10
-rw-r--r--tests/ui/proc-macro/issue-81555.rs3
-rw-r--r--tests/ui/pub/pub-ident-struct-4.fixed3
-rw-r--r--tests/ui/pub/pub-ident-struct-4.rs3
-rw-r--r--tests/ui/pub/pub-ident-struct-4.stderr6
-rw-r--r--tests/ui/regions/regions-issue-21422.rs1
-rw-r--r--tests/ui/structs-enums/newtype-struct-with-dtor.rs2
-rw-r--r--tests/ui/structs-enums/uninstantiable-struct.rs3
-rw-r--r--tests/ui/suggestions/derive-clone-for-eq.fixed1
-rw-r--r--tests/ui/suggestions/derive-clone-for-eq.rs1
-rw-r--r--tests/ui/suggestions/derive-clone-for-eq.stderr4
-rw-r--r--tests/ui/suggestions/option-content-move.fixed2
-rw-r--r--tests/ui/suggestions/option-content-move.rs2
-rw-r--r--tests/ui/suggestions/option-content-move.stderr4
-rw-r--r--tests/ui/traits/object/generics.rs1
-rw-r--r--tests/ui/unpretty/expanded-exhaustive.stdout3
324 files changed, 4409 insertions, 3158 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d2d4a7e3d42..521f8ef0f5a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -156,12 +156,6 @@ jobs:
 
       - name: checkout submodules
         run: src/ci/scripts/checkout-submodules.sh
-      
-      - name: Setup Python
-        uses: actions/setup-python@v5
-        with:
-          python-version: '3.x'
-        if: runner.environment == 'github-hosted'
 
       - name: install MinGW
         run: src/ci/scripts/install-mingw.sh
diff --git a/Cargo.lock b/Cargo.lock
index 0e950e28807..54b1bf593e0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2652,14 +2652,11 @@ version = "0.32.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
 dependencies = [
- "compiler_builtins",
  "crc32fast",
  "flate2",
  "hashbrown",
  "indexmap",
  "memchr",
- "rustc-std-workspace-alloc",
- "rustc-std-workspace-core",
  "ruzstd 0.5.0",
  "wasmparser",
 ]
@@ -2676,6 +2673,18 @@ dependencies = [
 ]
 
 [[package]]
+name = "object"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
+dependencies = [
+ "compiler_builtins",
+ "memchr",
+ "rustc-std-workspace-alloc",
+ "rustc-std-workspace-core",
+]
+
+[[package]]
 name = "odht"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4201,6 +4210,7 @@ dependencies = [
  "rustc_middle",
  "rustc_span",
  "rustc_target",
+ "rustc_type_ir",
  "smallvec",
  "tracing",
 ]
@@ -4392,7 +4402,6 @@ dependencies = [
  "rustc_hir_pretty",
  "rustc_index",
  "rustc_macros",
- "rustc_next_trait_solver",
  "rustc_query_system",
  "rustc_serialize",
  "rustc_session",
@@ -4509,6 +4518,7 @@ dependencies = [
  "rustc_serialize",
  "rustc_type_ir",
  "rustc_type_ir_macros",
+ "tracing",
 ]
 
 [[package]]
@@ -5356,7 +5366,7 @@ dependencies = [
  "hermit-abi",
  "libc",
  "miniz_oxide",
- "object 0.32.2",
+ "object 0.36.0",
  "panic_abort",
  "panic_unwind",
  "profiler_builtins",
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 910fbb87697..9cb193b4a67 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -488,6 +488,7 @@ pub struct Crate {
 /// E.g., `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]`.
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct MetaItem {
+    pub unsafety: Safety,
     pub path: Path,
     pub kind: MetaItemKind,
     pub span: Span,
@@ -1392,7 +1393,7 @@ pub enum ExprKind {
     /// An array (e.g, `[a, b, c, d]`).
     Array(ThinVec<P<Expr>>),
     /// Allow anonymous constants from an inline `const` block
-    ConstBlock(P<Expr>),
+    ConstBlock(AnonConst),
     /// A function call
     ///
     /// The first field resolves to the function itself,
@@ -2823,7 +2824,12 @@ pub struct NormalAttr {
 impl NormalAttr {
     pub fn from_ident(ident: Ident) -> Self {
         Self {
-            item: AttrItem { path: Path::from_ident(ident), args: AttrArgs::Empty, tokens: None },
+            item: AttrItem {
+                unsafety: Safety::Default,
+                path: Path::from_ident(ident),
+                args: AttrArgs::Empty,
+                tokens: None,
+            },
             tokens: None,
         }
     }
@@ -2831,6 +2837,7 @@ impl NormalAttr {
 
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct AttrItem {
+    pub unsafety: Safety,
     pub path: Path,
     pub args: AttrArgs,
     // Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`.
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index d5c9fc960c8..676a2377c3b 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -1,6 +1,8 @@
 //! Functions dealing with attributes and meta items.
 
-use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute};
+use crate::ast::{
+    AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, Safety,
+};
 use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit};
 use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem, NormalAttr};
 use crate::ast::{Path, PathSegment, DUMMY_NODE_ID};
@@ -238,7 +240,12 @@ impl AttrItem {
     }
 
     pub fn meta(&self, span: Span) -> Option<MetaItem> {
-        Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
+        Some(MetaItem {
+            unsafety: Safety::Default,
+            path: self.path.clone(),
+            kind: self.meta_kind()?,
+            span,
+        })
     }
 
     pub fn meta_kind(&self) -> Option<MetaItemKind> {
@@ -371,7 +378,10 @@ impl MetaItem {
             _ => path.span.hi(),
         };
         let span = path.span.with_hi(hi);
-        Some(MetaItem { path, kind, span })
+        // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`,
+        // but as a parenthesized list. This (and likely `MetaItem`) should be changed in
+        // such a way that builtin macros don't accept extraneous `unsafe()`.
+        Some(MetaItem { unsafety: Safety::Default, path, kind, span })
     }
 }
 
@@ -555,11 +565,12 @@ pub fn mk_doc_comment(
 pub fn mk_attr(
     g: &AttrIdGenerator,
     style: AttrStyle,
+    unsafety: Safety,
     path: Path,
     args: AttrArgs,
     span: Span,
 ) -> Attribute {
-    mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
+    mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span)
 }
 
 pub fn mk_attr_from_item(
@@ -577,15 +588,22 @@ pub fn mk_attr_from_item(
     }
 }
 
-pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
+pub fn mk_attr_word(
+    g: &AttrIdGenerator,
+    style: AttrStyle,
+    unsafety: Safety,
+    name: Symbol,
+    span: Span,
+) -> Attribute {
     let path = Path::from_ident(Ident::new(name, span));
     let args = AttrArgs::Empty;
-    mk_attr(g, style, path, args, span)
+    mk_attr(g, style, unsafety, path, args, span)
 }
 
 pub fn mk_attr_nested_word(
     g: &AttrIdGenerator,
     style: AttrStyle,
+    unsafety: Safety,
     outer: Symbol,
     inner: Symbol,
     span: Span,
@@ -601,12 +619,13 @@ pub fn mk_attr_nested_word(
         delim: Delimiter::Parenthesis,
         tokens: inner_tokens,
     });
-    mk_attr(g, style, path, attr_args, span)
+    mk_attr(g, style, unsafety, path, attr_args, span)
 }
 
 pub fn mk_attr_name_value_str(
     g: &AttrIdGenerator,
     style: AttrStyle,
+    unsafety: Safety,
     name: Symbol,
     val: Symbol,
     span: Span,
@@ -621,7 +640,7 @@ pub fn mk_attr_name_value_str(
     });
     let path = Path::from_ident(Ident::new(name, span));
     let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
-    mk_attr(g, style, path, args, span)
+    mk_attr(g, style, unsafety, path, args, span)
 }
 
 pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index a04c648ac73..cc33ce2cb56 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -647,8 +647,10 @@ fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
     let Attribute { kind, id: _, style: _, span } = attr;
     match kind {
         AttrKind::Normal(normal) => {
-            let NormalAttr { item: AttrItem { path, args, tokens }, tokens: attr_tokens } =
-                &mut **normal;
+            let NormalAttr {
+                item: AttrItem { unsafety: _, path, args, tokens },
+                tokens: attr_tokens,
+            } = &mut **normal;
             vis.visit_path(path);
             visit_attr_args(args, vis);
             visit_lazy_tts(tokens, vis);
@@ -678,7 +680,7 @@ fn noop_visit_meta_list_item<T: MutVisitor>(li: &mut NestedMetaItem, vis: &mut T
 }
 
 fn noop_visit_meta_item<T: MutVisitor>(mi: &mut MetaItem, vis: &mut T) {
-    let MetaItem { path: _, kind, span } = mi;
+    let MetaItem { unsafety: _, path: _, kind, span } = mi;
     match kind {
         MetaItemKind::Word => {}
         MetaItemKind::List(mis) => visit_thin_vec(mis, |mi| vis.visit_meta_list_item(mi)),
@@ -840,7 +842,7 @@ fn visit_nonterminal<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
         token::NtTy(ty) => vis.visit_ty(ty),
         token::NtLiteral(expr) => vis.visit_expr(expr),
         token::NtMeta(item) => {
-            let AttrItem { path, args, tokens } = item.deref_mut();
+            let AttrItem { unsafety: _, path, args, tokens } = item.deref_mut();
             vis.visit_path(path);
             visit_attr_args(args, vis);
             visit_lazy_tts(tokens, vis);
@@ -1417,7 +1419,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
     match kind {
         ExprKind::Array(exprs) => visit_thin_exprs(exprs, vis),
         ExprKind::ConstBlock(anon_const) => {
-            vis.visit_expr(anon_const);
+            vis.visit_anon_const(anon_const);
         }
         ExprKind::Repeat(expr, count) => {
             vis.visit_expr(expr);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index de285aed165..fa97c8db326 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -959,7 +959,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
         ExprKind::Array(subexpressions) => {
             walk_list!(visitor, visit_expr, subexpressions);
         }
-        ExprKind::ConstBlock(anon_const) => try_visit!(visitor.visit_expr(anon_const)),
+        ExprKind::ConstBlock(anon_const) => try_visit!(visitor.visit_anon_const(anon_const)),
         ExprKind::Repeat(element, count) => {
             try_visit!(visitor.visit_expr(element));
             try_visit!(visitor.visit_anon_const(count));
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index eb206a09be3..77f95869e9d 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -75,8 +75,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
             let kind = match &e.kind {
                 ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
                 ExprKind::ConstBlock(c) => {
-                    self.has_inline_consts = true;
-                    hir::ExprKind::ConstBlock(self.lower_expr(c))
+                    let c = self.with_new_scopes(c.value.span, |this| hir::ConstBlock {
+                        def_id: this.local_def_id(c.id),
+                        hir_id: this.lower_node_id(c.id),
+                        body: this.lower_const_body(c.value.span, Some(&c.value)),
+                    });
+                    hir::ExprKind::ConstBlock(c)
                 }
                 ExprKind::Repeat(expr, count) => {
                     let expr = self.lower_expr(expr);
@@ -1801,6 +1805,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let attr = attr::mk_attr_nested_word(
             &self.tcx.sess.psess.attr_id_generator,
             AttrStyle::Outer,
+            Safety::Default,
             sym::allow,
             sym::unreachable_code,
             self.lower_span(span),
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 741a44eb0c5..44f37b5533a 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -236,6 +236,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    fn visit_inline_const(&mut self, constant: &'hir ConstBlock) {
+        self.insert(DUMMY_SP, constant.hir_id, Node::ConstBlock(constant));
+
+        self.with_parent(constant.hir_id, |this| {
+            intravisit::walk_inline_const(this, constant);
+        });
+    }
+
     fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
         self.insert(expr.span, expr.hir_id, Node::Expr(expr));
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 023dc6d52c3..1c6b5b9af19 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -96,8 +96,6 @@ struct LoweringContext<'a, 'hir> {
 
     /// Bodies inside the owner being lowered.
     bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
-    /// Whether there were inline consts that typeck will split out into bodies
-    has_inline_consts: bool,
     /// Attributes inside the owner being lowered.
     attrs: SortedMap<hir::ItemLocalId, &'hir [Attribute]>,
     /// Collect items that were created by lowering the current owner.
@@ -160,7 +158,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             item_local_id_counter: hir::ItemLocalId::ZERO,
             node_id_to_local_id: Default::default(),
             trait_map: Default::default(),
-            has_inline_consts: false,
 
             // Lowering state.
             catch_scope: None,
@@ -570,7 +567,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         let current_attrs = std::mem::take(&mut self.attrs);
         let current_bodies = std::mem::take(&mut self.bodies);
-        let current_has_inline_consts = std::mem::take(&mut self.has_inline_consts);
         let current_node_ids = std::mem::take(&mut self.node_id_to_local_id);
         let current_trait_map = std::mem::take(&mut self.trait_map);
         let current_owner =
@@ -597,7 +593,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         self.attrs = current_attrs;
         self.bodies = current_bodies;
-        self.has_inline_consts = current_has_inline_consts;
         self.node_id_to_local_id = current_node_ids;
         self.trait_map = current_trait_map;
         self.current_hir_id_owner = current_owner;
@@ -634,7 +629,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let attrs = std::mem::take(&mut self.attrs);
         let mut bodies = std::mem::take(&mut self.bodies);
         let trait_map = std::mem::take(&mut self.trait_map);
-        let has_inline_consts = std::mem::take(&mut self.has_inline_consts);
 
         #[cfg(debug_assertions)]
         for (id, attrs) in attrs.iter() {
@@ -652,7 +646,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             self.tcx.hash_owner_nodes(node, &bodies, &attrs);
         let num_nodes = self.item_local_id_counter.as_usize();
         let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
-        let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies, has_inline_consts };
+        let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };
         let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
 
         self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
@@ -911,6 +905,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let kind = match attr.kind {
             AttrKind::Normal(ref normal) => AttrKind::Normal(P(NormalAttr {
                 item: AttrItem {
+                    unsafety: normal.item.unsafety,
                     path: normal.item.path.clone(),
                     args: self.lower_attr_args(&normal.item.args),
                     tokens: None,
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index a9dca9b6a29..764d942836c 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -561,6 +561,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(mut_ref, "mutable by-reference bindings are experimental");
     gate_all!(precise_capturing, "precise captures on `impl Trait` are experimental");
     gate_all!(global_registration, "global registration is experimental");
+    gate_all!(unsafe_attributes, "`#[unsafe()]` markers for attributes are experimental");
 
     if !visitor.features.never_patterns {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index ca26b436b82..f32b63a39f0 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -16,7 +16,7 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, To
 use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{Comment, CommentStyle};
-use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind};
+use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind, Safety};
 use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term};
 use rustc_ast::{GenericArg, GenericBound, SelfKind};
 use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
@@ -249,6 +249,7 @@ pub fn print_crate<'a>(
         let fake_attr = attr::mk_attr_nested_word(
             g,
             ast::AttrStyle::Inner,
+            Safety::Default,
             sym::feature,
             sym::prelude_import,
             DUMMY_SP,
@@ -259,7 +260,13 @@ pub fn print_crate<'a>(
         // root, so this is not needed, and actually breaks things.
         if edition.is_rust_2015() {
             // `#![no_std]`
-            let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP);
+            let fake_attr = attr::mk_attr_word(
+                g,
+                ast::AttrStyle::Inner,
+                Safety::Default,
+                sym::no_std,
+                DUMMY_SP,
+            );
             s.print_attribute(&fake_attr);
         }
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 993ccc5b956..1e117c46b6e 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -380,9 +380,8 @@ impl<'a> State<'a> {
             ast::ExprKind::Array(exprs) => {
                 self.print_expr_vec(exprs);
             }
-            ast::ExprKind::ConstBlock(expr) => {
-                self.word_space("const");
-                self.print_expr(expr, FixupContext::default());
+            ast::ExprKind::ConstBlock(anon_const) => {
+                self.print_expr_anon_const(anon_const, attrs);
             }
             ast::ExprKind::Repeat(element, count) => {
                 self.print_expr_repeat(element, count);
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index ff11e4db121..97408fa20d7 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -1,7 +1,7 @@
 use rustc_data_structures::graph::scc::Sccs;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
+use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
 use rustc_span::Span;
 use std::fmt;
 use std::ops::Index;
@@ -97,7 +97,7 @@ pub struct OutlivesConstraint<'tcx> {
     pub category: ConstraintCategory<'tcx>,
 
     /// Variance diagnostic information
-    pub variance_info: VarianceDiagInfo<'tcx>,
+    pub variance_info: VarianceDiagInfo<TyCtxt<'tcx>>,
 
     /// If this constraint is promoted from closure requirements.
     pub from_closure: bool,
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index b57cf9066cf..0e3140ca98b 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -2304,5 +2304,5 @@ pub struct BlameConstraint<'tcx> {
     pub category: ConstraintCategory<'tcx>,
     pub from_closure: bool,
     pub cause: ObligationCause<'tcx>,
-    pub variance_info: ty::VarianceDiagInfo<'tcx>,
+    pub variance_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
 }
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index 9863c4a3883..2c34fc583c8 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -1,14 +1,14 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
+use rustc_infer::infer::relate::{ObligationEmittingRelation, StructurallyRelateAliases};
+use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation};
 use rustc_infer::infer::NllRegionVariableOrigin;
-use rustc_infer::infer::{ObligationEmittingRelation, StructurallyRelateAliases};
 use rustc_infer::traits::{Obligation, PredicateObligations};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::span_bug;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::fold::FnMutDelegate;
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
@@ -82,7 +82,7 @@ pub struct NllTypeRelating<'me, 'bccx, 'tcx> {
     /// - Bivariant means that it doesn't matter.
     ambient_variance: ty::Variance,
 
-    ambient_variance_info: ty::VarianceDiagInfo<'tcx>,
+    ambient_variance_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
 }
 
 impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
@@ -296,7 +296,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
         &mut self,
         sup: ty::Region<'tcx>,
         sub: ty::Region<'tcx>,
-        info: ty::VarianceDiagInfo<'tcx>,
+        info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
     ) {
         let sub = self.type_checker.borrowck_context.universal_regions.to_region_vid(sub);
         let sup = self.type_checker.borrowck_context.universal_regions.to_region_vid(sup);
@@ -314,7 +314,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
     }
 }
 
-impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
+impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.type_checker.infcx.tcx
     }
@@ -324,10 +324,10 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
     }
 
     #[instrument(skip(self, info), level = "trace", ret)]
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        info: ty::VarianceDiagInfo<'tcx>,
+        info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -445,7 +445,7 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         // We want that
         //
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index a3d6a1c7360..2d1269e1b6a 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -110,6 +110,9 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a
 builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values
     .suggestion = remove the value
 
+builtin_macros_derive_unsafe_path = traits in `#[derive(...)]` don't accept `unsafe(...)`
+    .suggestion = remove the `unsafe(...)`
+
 builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
     .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
     .custom = use `std::env::var({$var_expr})` to read the variable at run time
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index e9b63b4abeb..16184ec7511 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -17,7 +17,7 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) {
         ));
 
         let start_span = parser.token.span;
-        let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) {
+        let AttrItem { unsafety, path, args, tokens: _ } = match parser.parse_attr_item(false) {
             Ok(ai) => ai,
             Err(err) => {
                 err.emit();
@@ -33,6 +33,7 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) {
         krate.attrs.push(mk_attr(
             &psess.attr_id_generator,
             AttrStyle::Inner,
+            unsafety,
             path,
             args,
             start_span.to(end_span),
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index d14858e5c1d..b5cbfdf0ec6 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -2,7 +2,7 @@ use crate::cfg_eval::cfg_eval;
 use crate::errors;
 
 use rustc_ast as ast;
-use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
+use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, Safety, StmtKind};
 use rustc_expand::base::{
     Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
 };
@@ -60,6 +60,7 @@ impl MultiItemModifier for Expander {
                                 // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
                                 // paths.
                                 report_path_args(sess, meta);
+                                report_unsafe_args(sess, meta);
                                 meta.path.clone()
                             })
                             .map(|path| DeriveResolution {
@@ -159,3 +160,13 @@ fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
         }
     }
 }
+
+fn report_unsafe_args(sess: &Session, meta: &ast::MetaItem) {
+    match meta.unsafety {
+        Safety::Unsafe(span) => {
+            sess.dcx().emit_err(errors::DeriveUnsafePath { span });
+        }
+        Safety::Default => {}
+        Safety::Safe(_) => unreachable!(),
+    }
+}
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index d157703723b..b14eb2b5ee6 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -296,6 +296,13 @@ pub(crate) struct DerivePathArgsValue {
 }
 
 #[derive(Diagnostic)]
+#[diag(builtin_macros_derive_unsafe_path)]
+pub(crate) struct DeriveUnsafePath {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(builtin_macros_no_default_variant)]
 #[help]
 pub(crate) struct NoDefaultVariant {
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 38ac2f15fe7..ba4e5cfd130 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -203,6 +203,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
                     let allow_dead_code = attr::mk_attr_nested_word(
                         &self.sess.psess.attr_id_generator,
                         ast::AttrStyle::Outer,
+                        ast::Safety::Default,
                         sym::allow,
                         sym::dead_code,
                         self.def_site,
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index 3c11d67e748..8c66888d100 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -38,6 +38,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
     match node {
         hir::Node::Ctor(_)
         | hir::Node::AnonConst(_)
+        | hir::Node::ConstBlock(_)
         | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => {
             hir::Constness::Const
         }
@@ -56,7 +57,6 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
             if is_const { hir::Constness::Const } else { hir::Constness::NotConst }
         }
         hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness,
-        hir::Node::Expr(e) if let hir::ExprKind::ConstBlock(_) = e.kind => hir::Constness::Const,
         _ => {
             if let Some(fn_kind) = node.fn_kind() {
                 if fn_kind.constness() == hir::Constness::Const {
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index b3d41908260..37dfd830512 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -666,7 +666,7 @@ impl<'a> ExtCtxt<'a> {
     // Builds `#[name]`.
     pub fn attr_word(&self, name: Symbol, span: Span) -> ast::Attribute {
         let g = &self.sess.psess.attr_id_generator;
-        attr::mk_attr_word(g, ast::AttrStyle::Outer, name, span)
+        attr::mk_attr_word(g, ast::AttrStyle::Outer, ast::Safety::Default, name, span)
     }
 
     // Builds `#[name = val]`.
@@ -674,12 +674,26 @@ impl<'a> ExtCtxt<'a> {
     // Note: `span` is used for both the identifier and the value.
     pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute {
         let g = &self.sess.psess.attr_id_generator;
-        attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span)
+        attr::mk_attr_name_value_str(
+            g,
+            ast::AttrStyle::Outer,
+            ast::Safety::Default,
+            name,
+            val,
+            span,
+        )
     }
 
     // Builds `#[outer(inner)]`.
     pub fn attr_nested_word(&self, outer: Symbol, inner: Symbol, span: Span) -> ast::Attribute {
         let g = &self.sess.psess.attr_id_generator;
-        attr::mk_attr_nested_word(g, ast::AttrStyle::Outer, outer, inner, span)
+        attr::mk_attr_nested_word(
+            g,
+            ast::AttrStyle::Outer,
+            ast::Safety::Default,
+            outer,
+            inner,
+            span,
+        )
     }
 }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index d8f0f221189..c28a09eb57c 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -778,7 +778,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     if let SyntaxExtensionKind::Derive(..) = ext {
                         self.gate_proc_macro_input(&item);
                     }
-                    let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path };
+                    // The `MetaItem` representing the trait to derive can't
+                    // have an unsafe around it (as of now).
+                    let meta = ast::MetaItem {
+                        unsafety: ast::Safety::Default,
+                        kind: MetaItemKind::Word,
+                        span,
+                        path,
+                    };
                     let items = match expander.expand(self.cx, span, &meta, item, is_const) {
                         ExpandResult::Ready(items) => items,
                         ExpandResult::Retry(item) => {
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 8b7e93fd555..9b5e4e50d3c 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -59,6 +59,16 @@ pub enum AttributeType {
     CrateLevel,
 }
 
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum AttributeSafety {
+    /// Normal attribute that does not need `#[unsafe(...)]`
+    Normal,
+
+    /// Unsafe attribute that requires safety obligations
+    /// to be discharged
+    Unsafe,
+}
+
 #[derive(Clone, Copy)]
 pub enum AttributeGate {
     /// Is gated by a given feature gate, reason
@@ -172,11 +182,23 @@ macro_rules! template {
 }
 
 macro_rules! ungated {
+    (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => {
+        BuiltinAttribute {
+            name: sym::$attr,
+            encode_cross_crate: $encode_cross_crate,
+            type_: $typ,
+            safety: AttributeSafety::Unsafe,
+            template: $tpl,
+            gate: Ungated,
+            duplicates: $duplicates,
+        }
+    };
     ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
             type_: $typ,
+            safety: AttributeSafety::Normal,
             template: $tpl,
             gate: Ungated,
             duplicates: $duplicates,
@@ -185,11 +207,34 @@ macro_rules! ungated {
 }
 
 macro_rules! gated {
+    (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
+        BuiltinAttribute {
+            name: sym::$attr,
+            encode_cross_crate: $encode_cross_crate,
+            type_: $typ,
+            safety: AttributeSafety::Unsafe,
+            template: $tpl,
+            duplicates: $duplicates,
+            gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
+        }
+    };
+    (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
+        BuiltinAttribute {
+            name: sym::$attr,
+            encode_cross_crate: $encode_cross_crate,
+            type_: $typ,
+            safety: AttributeSafety::Unsafe,
+            template: $tpl,
+            duplicates: $duplicates,
+            gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
+        }
+    };
     ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
         BuiltinAttribute {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
             type_: $typ,
+            safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
             gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
@@ -200,6 +245,7 @@ macro_rules! gated {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
             type_: $typ,
+            safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
             gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
@@ -228,6 +274,7 @@ macro_rules! rustc_attr {
             name: sym::$attr,
             encode_cross_crate: $encode_cross_crate,
             type_: $typ,
+            safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
             gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)),
@@ -258,6 +305,7 @@ pub struct BuiltinAttribute {
     /// Otherwise, it can only be used in the local crate.
     pub encode_cross_crate: EncodeCrossCrate,
     pub type_: AttributeType,
+    pub safety: AttributeSafety,
     pub template: AttributeTemplate,
     pub duplicates: AttributeDuplicates,
     pub gate: AttributeGate,
@@ -375,9 +423,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
     ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No),
-    ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
-    ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
-    ungated!(no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
+    ungated!(unsafe export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
+    ungated!(unsafe link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
+    ungated!(unsafe no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
     ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No),
     ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes),
 
@@ -486,11 +534,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
 
     gated!(
-        ffi_pure, Normal, template!(Word), WarnFollowing,
+        unsafe ffi_pure, Normal, template!(Word), WarnFollowing,
         EncodeCrossCrate::No, experimental!(ffi_pure)
     ),
     gated!(
-        ffi_const, Normal, template!(Word), WarnFollowing,
+        unsafe ffi_const, Normal, template!(Word), WarnFollowing,
         EncodeCrossCrate::No, experimental!(ffi_const)
     ),
     gated!(
@@ -850,6 +898,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
         encode_cross_crate: EncodeCrossCrate::Yes,
         type_: Normal,
+        safety: AttributeSafety::Normal,
         template: template!(NameValueStr: "name"),
         duplicates: ErrorFollowing,
         gate: Gated(
@@ -1096,6 +1145,10 @@ pub fn is_valid_for_get_attr(name: Symbol) -> bool {
     })
 }
 
+pub fn is_unsafe_attr(name: Symbol) -> bool {
+    BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.safety == AttributeSafety::Unsafe)
+}
+
 pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>> =
     LazyLock::new(|| {
         let mut map = FxHashMap::default();
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 9cbf836ec76..9db9073e2f0 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -123,8 +123,8 @@ pub use accepted::ACCEPTED_FEATURES;
 pub use builtin_attrs::AttributeDuplicates;
 pub use builtin_attrs::{
     deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name,
-    is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType, BuiltinAttribute,
-    GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
+    is_unsafe_attr, is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType,
+    BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
 };
 pub use removed::REMOVED_FEATURES;
 pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES};
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 8add2b92761..d67422849d8 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -620,6 +620,8 @@ declare_features! (
     (unstable, type_changing_struct_update, "1.58.0", Some(86555)),
     /// Allows unnamed fields of struct and union type
     (incomplete, unnamed_fields, "1.74.0", Some(49804)),
+    /// Allows unsafe attributes.
+    (unstable, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)),
     /// Allows unsafe on extern declarations and safety qualifiers over internal items.
     (unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)),
     /// Allows unsized fn parameters.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 770dfcb98c9..87ff39a8294 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -907,9 +907,6 @@ pub struct OwnerNodes<'tcx> {
     pub nodes: IndexVec<ItemLocalId, ParentedNode<'tcx>>,
     /// Content of local bodies.
     pub bodies: SortedMap<ItemLocalId, &'tcx Body<'tcx>>,
-    /// Whether the body contains inline constants that are created for the query system during typeck
-    /// of the body.
-    pub has_inline_consts: bool,
 }
 
 impl<'tcx> OwnerNodes<'tcx> {
@@ -1626,6 +1623,14 @@ pub struct AnonConst {
     pub span: Span,
 }
 
+/// An inline constant expression `const { something }`.
+#[derive(Copy, Clone, Debug, HashStable_Generic)]
+pub struct ConstBlock {
+    pub hir_id: HirId,
+    pub def_id: LocalDefId,
+    pub body: BodyId,
+}
+
 /// An expression.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Expr<'hir> {
@@ -1912,7 +1917,7 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool {
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub enum ExprKind<'hir> {
     /// Allow anonymous constants from an inline `const` block
-    ConstBlock(&'hir Expr<'hir>),
+    ConstBlock(ConstBlock),
     /// An array (e.g., `[a, b, c, d]`).
     Array(&'hir [Expr<'hir>]),
     /// A function call.
@@ -2345,7 +2350,9 @@ impl ImplItemId {
     }
 }
 
-/// Represents anything within an `impl` block.
+/// Represents an associated item within an impl block.
+///
+/// Refer to [`Impl`] for an impl block declaration.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct ImplItem<'hir> {
     pub ident: Ident,
@@ -3327,6 +3334,10 @@ pub enum ItemKind<'hir> {
     Impl(&'hir Impl<'hir>),
 }
 
+/// Represents an impl block declaration.
+///
+/// E.g., `impl $Type { .. }` or `impl $Trait for $Type { .. }`
+/// Refer to [`ImplItem`] for an associated item within an impl block.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Impl<'hir> {
     pub safety: Safety,
@@ -3644,6 +3655,7 @@ pub enum Node<'hir> {
     Variant(&'hir Variant<'hir>),
     Field(&'hir FieldDef<'hir>),
     AnonConst(&'hir AnonConst),
+    ConstBlock(&'hir ConstBlock),
     Expr(&'hir Expr<'hir>),
     ExprField(&'hir ExprField<'hir>),
     Stmt(&'hir Stmt<'hir>),
@@ -3704,6 +3716,7 @@ impl<'hir> Node<'hir> {
             Node::PreciseCapturingNonLifetimeArg(a) => Some(a.ident),
             Node::Param(..)
             | Node::AnonConst(..)
+            | Node::ConstBlock(..)
             | Node::Expr(..)
             | Node::Stmt(..)
             | Node::Block(..)
@@ -3801,6 +3814,7 @@ impl<'hir> Node<'hir> {
             }
 
             Node::AnonConst(constant) => Some((constant.def_id, constant.body)),
+            Node::ConstBlock(constant) => Some((constant.def_id, constant.body)),
 
             _ => None,
         }
@@ -3869,6 +3883,7 @@ impl<'hir> Node<'hir> {
         expect_variant,       &'hir Variant<'hir>,      Node::Variant(n),      n;
         expect_field,         &'hir FieldDef<'hir>,     Node::Field(n),        n;
         expect_anon_const,    &'hir AnonConst,          Node::AnonConst(n),    n;
+        expect_inline_const,  &'hir ConstBlock,         Node::ConstBlock(n),   n;
         expect_expr,          &'hir Expr<'hir>,         Node::Expr(n),         n;
         expect_expr_field,    &'hir ExprField<'hir>,    Node::ExprField(n),    n;
         expect_stmt,          &'hir Stmt<'hir>,         Node::Stmt(n),         n;
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index e37473df956..5a16f266dab 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -344,6 +344,9 @@ pub trait Visitor<'v>: Sized {
     fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result {
         walk_anon_const(self, c)
     }
+    fn visit_inline_const(&mut self, c: &'v ConstBlock) -> Self::Result {
+        walk_inline_const(self, c)
+    }
     fn visit_expr(&mut self, ex: &'v Expr<'v>) -> Self::Result {
         walk_expr(self, ex)
     }
@@ -718,6 +721,14 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo
     visitor.visit_nested_body(constant.body)
 }
 
+pub fn walk_inline_const<'v, V: Visitor<'v>>(
+    visitor: &mut V,
+    constant: &'v ConstBlock,
+) -> V::Result {
+    try_visit!(visitor.visit_id(constant.hir_id));
+    visitor.visit_nested_body(constant.body)
+}
+
 pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) -> V::Result {
     try_visit!(visitor.visit_id(expression.hir_id));
     match expression.kind {
@@ -725,7 +736,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
             walk_list!(visitor, visit_expr, subexpressions);
         }
         ExprKind::ConstBlock(ref const_block) => {
-            try_visit!(visitor.visit_expr(const_block))
+            try_visit!(visitor.visit_inline_const(const_block))
         }
         ExprKind::Repeat(ref element, ref count) => {
             try_visit!(visitor.visit_expr(element));
diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs
index 1ebd4b80e18..baa1635f731 100644
--- a/compiler/rustc_hir/src/stable_hash_impls.rs
+++ b/compiler/rustc_hir/src/stable_hash_impls.rs
@@ -93,8 +93,7 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable<HirCtx> for OwnerNodes<'
         // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing
         // the body satisfies the condition of two nodes being different have different
         // `hash_stable` results.
-        let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _, has_inline_consts: _ } =
-            *self;
+        let OwnerNodes { opt_hash_including_bodies, nodes: _, bodies: _ } = *self;
         opt_hash_including_bodies.unwrap().hash_stable(hcx, hasher);
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 8a0623ef93e..72e5995e892 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -15,6 +15,7 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
 use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::span_bug;
+use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt};
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 30b03b43872..72e431926ca 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -407,14 +407,11 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
     match expr.kind {
         // Manually recurse over closures and inline consts, because they are the only
         // case of nested bodies that share the parent environment.
-        hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
+        hir::ExprKind::Closure(&hir::Closure { body, .. })
+        | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) => {
             let body = visitor.tcx.hir().body(body);
             visitor.visit_body(body);
         }
-        hir::ExprKind::ConstBlock(expr) => visitor.enter_body(expr.hir_id, |this| {
-            this.cx.var_parent = None;
-            resolve_local(this, None, Some(expr));
-        }),
         hir::ExprKind::AssignOp(_, left_expr, right_expr) => {
             debug!(
                 "resolve_expr - enabling pessimistic_yield, was previously {}",
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 9af959681fb..abdf85ad707 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -177,10 +177,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 }
             }
         }
-        Node::Expr(&hir::Expr {
-            kind: hir::ExprKind::Closure { .. } | hir::ExprKind::ConstBlock { .. },
-            ..
-        }) => Some(tcx.typeck_root_def_id(def_id.to_def_id())),
+        Node::ConstBlock(_)
+        | Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
+            Some(tcx.typeck_root_def_id(def_id.to_def_id()))
+        }
         Node::Item(item) => match item.kind {
             ItemKind::OpaqueTy(&hir::OpaqueTy {
                 origin:
@@ -415,7 +415,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
     }
 
     // provide junk type parameter defs for const blocks.
-    if let Node::Expr(Expr { kind: ExprKind::ConstBlock(..), .. }) = node {
+    if let Node::ConstBlock(_) = node {
         own_params.push(ty::GenericParamDef {
             index: next_index(),
             name: Symbol::intern("<const_ty>"),
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 6811f62de07..2684467a438 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -485,7 +485,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
         }
 
         Node::AnonConst(_) => anon_const_type_of(tcx, def_id),
-        Node::Expr(&Expr { kind: ExprKind::ConstBlock(..), .. }) => {
+
+        Node::ConstBlock(_) => {
             let args = ty::GenericArgs::identity_for_item(tcx, def_id.to_def_id());
             args.as_inline_const().ty()
         }
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 65b02a2ec56..8fe81851f93 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -190,6 +190,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
         }
     });
 
+    // Freeze definitions as we don't add new ones at this point. This improves performance by
+    // allowing lock-free access to them.
+    tcx.untracked().definitions.freeze();
+
     // FIXME: Remove this when we implement creating `DefId`s
     // for anon constants during their parents' typeck.
     // Typeck all body owners in parallel will produce queries
@@ -201,10 +205,6 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
         }
     });
 
-    // Freeze definitions as we don't add new ones at this point. This improves performance by
-    // allowing lock-free access to them.
-    tcx.untracked().definitions.freeze();
-
     tcx.ensure().check_unused_traits(());
 }
 
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 6983bbcb052..6f2febd86b0 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -84,6 +84,7 @@ impl<'a> State<'a> {
             Node::ImplItem(a) => self.print_impl_item(a),
             Node::Variant(a) => self.print_variant(a),
             Node::AnonConst(a) => self.print_anon_const(a),
+            Node::ConstBlock(a) => self.print_inline_const(a),
             Node::Expr(a) => self.print_expr(a),
             Node::ExprField(a) => self.print_expr_field(a),
             Node::Stmt(a) => self.print_stmt(a),
@@ -1049,10 +1050,10 @@ impl<'a> State<'a> {
         self.end()
     }
 
-    fn print_inline_const(&mut self, constant: &hir::Expr<'_>) {
+    fn print_inline_const(&mut self, constant: &hir::ConstBlock) {
         self.ibox(INDENT_UNIT);
         self.word_space("const");
-        self.print_expr(constant);
+        self.ann.nested(self, Nested::Body(constant.body));
         self.end()
     }
 
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 93726ce2b3e..dbaa6e398c8 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -41,6 +41,7 @@ use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
+use rustc_infer::infer::relate::RelateResult;
 use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
 use rustc_infer::traits::{IfExpressionCause, MatchExpressionArmCause};
 use rustc_infer::traits::{Obligation, PredicateObligation};
@@ -51,7 +52,6 @@ use rustc_middle::ty::adjustment::{
     Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
 };
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
 use rustc_session::parse::feature_err;
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 7dd7b3ff055..d5d360ca047 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -31,6 +31,7 @@ use rustc_errors::{
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, HirId, QPath};
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _;
@@ -40,7 +41,7 @@ use rustc_infer::infer::InferOk;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
-use rustc_middle::ty::error::{ExpectedFound, TypeError::Sorts};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
@@ -334,7 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected),
             ExprKind::Array(args) => self.check_expr_array(args, expected, expr),
-            ExprKind::ConstBlock(ref block) => self.check_expr_with_expectation(block, expected),
+            ExprKind::ConstBlock(ref block) => self.check_expr_const_block(block, expected),
             ExprKind::Repeat(element, ref count) => {
                 self.check_expr_repeat(element, count, expected, expr)
             }
@@ -682,7 +683,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.suggest_mismatched_types_on_tail(
                                 &mut err, expr, ty, e_ty, target_id,
                             );
-                            let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty }));
+                            let error =
+                                Some(TypeError::Sorts(ExpectedFound { expected: ty, found: e_ty }));
                             self.annotate_loop_expected_due_to_inference(err, expr, error);
                             if let Some(val) =
                                 self.err_ctxt().ty_kind_suggestion(self.param_env, ty)
@@ -1456,6 +1458,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn check_expr_const_block(
+        &self,
+        block: &'tcx hir::ConstBlock,
+        expected: Expectation<'tcx>,
+    ) -> Ty<'tcx> {
+        let body = self.tcx.hir().body(block.body);
+
+        // Create a new function context.
+        let def_id = block.def_id;
+        let fcx = FnCtxt::new(self, self.param_env, def_id);
+        crate::GatherLocalsVisitor::new(&fcx).visit_body(body);
+
+        let ty = fcx.check_expr_with_expectation(body.value, expected);
+        fcx.require_type_is_sized(ty, body.value.span, ObligationCauseCode::ConstSized);
+        fcx.write_ty(block.hir_id, ty);
+        ty
+    }
+
     fn check_expr_repeat(
         &self,
         element: &'tcx hir::Expr<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index caaf4142f7d..9ab89f3444c 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1051,10 +1051,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .take_while(|(_, node)| {
                     // look at parents until we find the first body owner
                     node.body_id().is_none()
-                        && !matches!(
-                            node,
-                            Node::Expr(Expr { kind: ExprKind::ConstBlock { .. }, .. })
-                        )
                 })
                 .any(|(parent_id, _)| self.is_loop(parent_id));
 
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 4386e68ce86..466397817da 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -149,6 +149,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> {
                 self.visit_body(body);
                 self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause);
             }
+            hir::ExprKind::ConstBlock(anon_const) => {
+                let body = self.fcx.tcx.hir().body(anon_const.body);
+                self.visit_body(body);
+            }
             _ => {}
         }
 
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 8727c0f87dc..b67d29fce92 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -3,7 +3,6 @@
 // generic parameters.
 
 use crate::FnCtxt;
-use hir::def::DefKind;
 use rustc_data_structures::unord::ExtendUnord;
 use rustc_errors::{ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
@@ -17,7 +16,7 @@ use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::TypeSuperFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::symbol::{kw, sym};
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_trait_selection::solve;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
@@ -296,11 +295,11 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
             hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => {
                 self.visit_field_id(e.hir_id);
             }
-            hir::ExprKind::ConstBlock(_) => {
-                let feed = self.tcx().create_def(self.fcx.body_id, kw::Empty, DefKind::InlineConst);
-                feed.def_span(e.span);
-                feed.local_def_id_to_hir_id(e.hir_id);
-                self.typeck_results.inline_consts.insert(e.hir_id.local_id, feed.def_id());
+            hir::ExprKind::ConstBlock(anon_const) => {
+                self.visit_node_id(e.span, anon_const.hir_id);
+
+                let body = self.tcx().hir().body(anon_const.body);
+                self.visit_body(body);
             }
             _ => {}
         }
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index c1565a7d40f..5136ab79a0f 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -18,6 +18,7 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
+rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 17e6d6250ad..046d908d148 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -27,8 +27,8 @@
 
 use super::*;
 
+use crate::infer::relate::{Relate, StructurallyRelateAliases, TypeRelation};
 use rustc_middle::bug;
-use rustc_middle::ty::relate::{Relate, TypeRelation};
 use rustc_middle::ty::{Const, ImplSubject};
 
 /// Whether we should define opaque types or just treat them opaquely.
@@ -90,7 +90,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 }
 
-pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
+pub trait ToTrace<'tcx>: Relate<TyCtxt<'tcx>> + Copy {
     fn to_trace(
         cause: &ObligationCause<'tcx>,
         a_is_expected: bool,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index fe0a246abbc..ed483c6cbeb 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -58,6 +58,7 @@ use crate::traits::{
     PredicateObligation,
 };
 
+use crate::infer::relate::{self, RelateResult, TypeRelation};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{
     codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString,
@@ -71,8 +72,8 @@ use rustc_hir::lang_items::LangItem;
 use rustc_macros::extension;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::DepContext;
+use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _};
-use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
 use rustc_middle::ty::Upcast;
 use rustc_middle::ty::{
     self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
@@ -2686,7 +2687,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
     /// FloatVar inference type are compatible with themselves or their concrete types (Int and
     /// Float types, respectively). When comparing two ADTs, these rules apply recursively.
-    pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
+    pub fn same_type_modulo_infer<T: relate::Relate<TyCtxt<'tcx>>>(&self, a: T, b: T) -> bool {
         let (a, b) = self.resolve_vars_if_possible((a, b));
         SameTypeModuloInfer(self).relate(a, b).is_ok()
     }
@@ -2694,7 +2695,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
 struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>);
 
-impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameTypeModuloInfer<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.0.tcx
     }
@@ -2703,10 +2704,10 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
         "SameTypeModuloInfer"
     }
 
-    fn relate_with_variance<T: relate::Relate<'tcx>>(
+    fn relate_with_variance<T: relate::Relate<TyCtxt<'tcx>>>(
         &mut self,
         _variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> relate::RelateResult<'tcx, T> {
@@ -2754,7 +2755,7 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: relate::Relate<'tcx>,
+        T: relate::Relate<TyCtxt<'tcx>>,
     {
         Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
     }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index 19ef2d61fca..b88677b3a4e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -21,13 +21,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         sp: Span,
         body_owner_def_id: DefId,
     ) {
-        use ty::error::TypeError::*;
         debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
 
         let tcx = self.tcx;
 
         match err {
-            ArgumentSorts(values, _) | Sorts(values) => {
+            TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
                 match (*values.expected.kind(), *values.found.kind()) {
                     (ty::Closure(..), ty::Closure(..)) => {
                         diag.note("no two closures, even if identical, have the same type");
@@ -483,7 +482,7 @@ impl<T> Trait<T> for X {
                     values.found.kind(),
                 );
             }
-            CyclicTy(ty) => {
+            TypeError::CyclicTy(ty) => {
                 // Watch out for various cases of cyclic types and try to explain.
                 if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
                     diag.note(
@@ -494,7 +493,7 @@ impl<T> Trait<T> for X {
                     );
                 }
             }
-            TargetFeatureCast(def_id) => {
+            TypeError::TargetFeatureCast(def_id) => {
                 let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
                 diag.note(
                     "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 4476611d9c8..c606ab808ef 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1,9 +1,6 @@
 pub use at::DefineOpaqueTypes;
 pub use freshen::TypeFreshener;
 pub use lexical_region_resolve::RegionResolutionError;
-pub use relate::combine::CombineFields;
-pub use relate::combine::ObligationEmittingRelation;
-pub use relate::StructurallyRelateAliases;
 pub use rustc_macros::{TypeFoldable, TypeVisitable};
 pub use rustc_middle::ty::IntVarValue;
 pub use BoundRegionConversionTime::*;
@@ -11,6 +8,7 @@ pub use RegionVariableOrigin::*;
 pub use SubregionOrigin::*;
 pub use ValuePairs::*;
 
+use crate::infer::relate::{CombineFields, RelateResult};
 use crate::traits::{
     self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
 };
@@ -39,7 +37,6 @@ use rustc_middle::traits::select;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::BoundVarReplacerDelegate;
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
-use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt};
 use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
@@ -62,7 +59,7 @@ pub mod opaque_types;
 pub mod outlives;
 mod projection;
 pub mod region_constraints;
-mod relate;
+pub mod relate;
 pub mod resolve;
 pub(crate) mod snapshot;
 pub mod type_variable;
diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
index 29c11d4247d..978b92fd898 100644
--- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
+++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
@@ -1,15 +1,12 @@
 use std::collections::hash_map::Entry;
 
 use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::TypeVisitableExt;
-use rustc_middle::ty::{
-    self,
-    error::TypeError,
-    relate::{self, Relate, RelateResult, TypeRelation},
-    Ty, TyCtxt,
-};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 
 use crate::infer::region_constraints::VerifyIfEq;
+use crate::infer::relate::{self as relate, Relate, RelateResult, TypeRelation};
 
 /// Given a "verify-if-eq" type test like:
 ///
@@ -135,7 +132,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstHigherRankedOutlives<'tcx> {
     fn tag(&self) -> &'static str {
         "MatchAgainstHigherRankedOutlives"
     }
@@ -145,10 +142,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _: ty::VarianceDiagInfo<'tcx>,
+        _: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -208,7 +205,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> {
         value: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         self.pattern_depth.shift_in(1);
         let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?));
diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
index 255ca52d3e9..5b159d62731 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
@@ -1,11 +1,11 @@
 use super::*;
+use crate::infer::relate::RelateResult;
 use crate::infer::snapshot::CombinedSnapshot;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::graph::{scc::Sccs, vec_graph::VecGraph};
 use rustc_index::Idx;
 use rustc_middle::span_bug;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::RelateResult;
 
 impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
     /// Searches new universes created during `snapshot`, looking for
@@ -276,7 +276,7 @@ impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
         other_region: ty::Region<'tcx>,
     ) -> TypeError<'tcx> {
         debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
-        TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region)
+        TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound, other_region)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_infer/src/infer/relate/_match.rs
index f30270abd5c..30a066a265a 100644
--- a/compiler/rustc_middle/src/ty/_match.rs
+++ b/compiler/rustc_infer/src/infer/relate/_match.rs
@@ -1,8 +1,10 @@
-use crate::ty::error::TypeError;
-use crate::ty::relate::{self, Relate, RelateResult, TypeRelation};
-use crate::ty::{self, InferConst, Ty, TyCtxt};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
 use tracing::{debug, instrument};
 
+use super::{structurally_relate_tys, Relate, RelateResult, TypeRelation};
+use crate::infer::relate;
+
 /// A type "A" *matches* "B" if the fresh types in B could be
 /// instantiated with values so as to make it equal to A. Matching is
 /// intended to be used only on freshened types, and it basically
@@ -29,7 +31,7 @@ impl<'tcx> MatchAgainstFreshVars<'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> {
     fn tag(&self) -> &'static str {
         "MatchAgainstFreshVars"
     }
@@ -38,10 +40,10 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
         self.tcx
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         _: ty::Variance,
-        _: ty::VarianceDiagInfo<'tcx>,
+        _: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -72,12 +74,12 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
             ) => Ok(a),
 
             (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-                Err(TypeError::Sorts(relate::expected_found(a, b)))
+                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
             }
 
             (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)),
 
-            _ => relate::structurally_relate_tys(self, a, b),
+            _ => structurally_relate_tys(self, a, b),
         }
     }
 
@@ -97,7 +99,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
             }
 
             (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
-                return Err(TypeError::ConstMismatch(relate::expected_found(a, b)));
+                return Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b)));
             }
 
             _ => {}
@@ -112,7 +114,7 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
     }
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index b193f4bcede..30cb2bab900 100644
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -22,12 +22,13 @@ use super::glb::Glb;
 use super::lub::Lub;
 use super::type_relating::TypeRelating;
 use super::StructurallyRelateAliases;
+use super::{RelateResult, TypeRelation};
+use crate::infer::relate;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::traits::{Obligation, PredicateObligations};
 use rustc_middle::bug;
 use rustc_middle::infer::unify_key::EffectVarValue;
-use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::{RelateResult, TypeRelation};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast};
 use rustc_middle::ty::{IntType, UintType};
 use rustc_span::Span;
@@ -121,7 +122,7 @@ impl<'tcx> InferCtxt<'tcx> {
             (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
                 match relation.structurally_relate_aliases() {
                     StructurallyRelateAliases::Yes => {
-                        ty::relate::structurally_relate_tys(relation, a, b)
+                        relate::structurally_relate_tys(relation, a, b)
                     }
                     StructurallyRelateAliases::No => {
                         relation.register_type_relate_obligation(a, b);
@@ -132,7 +133,7 @@ impl<'tcx> InferCtxt<'tcx> {
 
             // All other cases of inference are errors
             (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-                Err(TypeError::Sorts(ty::relate::expected_found(a, b)))
+                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
             }
 
             // During coherence, opaque types should be treated as *possibly*
@@ -144,7 +145,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 Ok(a)
             }
 
-            _ => ty::relate::structurally_relate_tys(relation, a, b),
+            _ => relate::structurally_relate_tys(relation, a, b),
         }
     }
 
@@ -234,11 +235,11 @@ impl<'tcx> InferCtxt<'tcx> {
                         Ok(b)
                     }
                     StructurallyRelateAliases::Yes => {
-                        ty::relate::structurally_relate_consts(relation, a, b)
+                        relate::structurally_relate_consts(relation, a, b)
                     }
                 }
             }
-            _ => ty::relate::structurally_relate_consts(relation, a, b),
+            _ => relate::structurally_relate_consts(relation, a, b),
         }
     }
 
@@ -303,7 +304,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
     }
 }
 
-pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
+pub trait ObligationEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> {
     fn span(&self) -> Span;
 
     fn param_env(&self) -> ty::ParamEnv<'tcx>;
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index 225c126fcf8..5478afda455 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -1,15 +1,16 @@
 use std::mem;
 
 use super::StructurallyRelateAliases;
+use super::{ObligationEmittingRelation, Relate, RelateResult, TypeRelation};
+use crate::infer::relate;
 use crate::infer::type_variable::TypeVariableValue;
-use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin};
+use crate::infer::{InferCtxt, RegionVariableOrigin};
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::def_id::DefId;
 use rustc_middle::bug;
 use rustc_middle::infer::unify_key::ConstVariableValue;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::visit::MaxUniverse;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{AliasRelationDirection, InferConst, Term, TypeVisitable, TypeVisitableExt};
@@ -228,7 +229,7 @@ impl<'tcx> InferCtxt<'tcx> {
 
     /// Attempts to generalize `source_term` for the type variable `target_vid`.
     /// This checks for cycles -- that is, whether `source_term` references `target_vid`.
-    fn generalize<T: Into<Term<'tcx>> + Relate<'tcx>>(
+    fn generalize<T: Into<Term<'tcx>> + Relate<TyCtxt<'tcx>>>(
         &self,
         span: Span,
         structurally_relate_aliases: StructurallyRelateAliases,
@@ -395,7 +396,7 @@ impl<'tcx> Generalizer<'_, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.infcx.tcx
     }
@@ -430,10 +431,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
     }
 
     #[instrument(level = "debug", skip(self, variance, b), ret)]
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -695,7 +696,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
         _: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         let result = self.relate(a.skip_binder(), a.skip_binder())?;
         Ok(a.rebind(result))
diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index a224a86492a..98e8f07c7a2 100644
--- a/compiler/rustc_infer/src/infer/relate/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -1,6 +1,6 @@
 //! Greatest lower bound. See [`lattice`].
 
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use super::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::Span;
 
@@ -21,7 +21,7 @@ impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Glb<'_, '_, 'tcx> {
     fn tag(&self) -> &'static str {
         "Glb"
     }
@@ -30,10 +30,10 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
         self.fields.tcx()
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         // GLB of a binder and itself is just itself
         if a == b {
diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
index d3001eb5838..cfce28aca5d 100644
--- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
+++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
@@ -1,10 +1,10 @@
 //! Helper routines for higher-ranked things. See the `doc` module at
 //! the end of the file for details.
 
+use super::RelateResult;
 use crate::infer::snapshot::CombinedSnapshot;
 use crate::infer::InferCtxt;
 use rustc_middle::ty::fold::FnMutDelegate;
-use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 
 impl<'tcx> InferCtxt<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs
index c0c51a2820b..f05b984142a 100644
--- a/compiler/rustc_infer/src/infer/relate/lattice.rs
+++ b/compiler/rustc_infer/src/infer/relate/lattice.rs
@@ -21,7 +21,7 @@ use super::combine::ObligationEmittingRelation;
 use crate::infer::{DefineOpaqueTypes, InferCtxt};
 use crate::traits::ObligationCause;
 
-use rustc_middle::ty::relate::RelateResult;
+use super::RelateResult;
 use rustc_middle::ty::TyVar;
 use rustc_middle::ty::{self, Ty};
 
diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index 83ab7770770..28dbaa94f95 100644
--- a/compiler/rustc_infer/src/infer/relate/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -6,7 +6,7 @@ use super::StructurallyRelateAliases;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
 use crate::traits::{ObligationCause, PredicateObligations};
 
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use super::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::Span;
 
@@ -21,7 +21,7 @@ impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Lub<'_, '_, 'tcx> {
     fn tag(&self) -> &'static str {
         "Lub"
     }
@@ -30,10 +30,10 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
         self.fields.tcx()
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         // LUB of a binder and itself is just itself
         if a == b {
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index 86a01130167..627c527cba1 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -1,6 +1,14 @@
 //! This module contains the definitions of most `TypeRelation`s in the type system
 //! (except for some relations used for diagnostics and heuristics in the compiler).
+//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc).
 
+pub use rustc_middle::ty::relate::*;
+
+pub use self::_match::MatchAgainstFreshVars;
+pub use self::combine::CombineFields;
+pub use self::combine::ObligationEmittingRelation;
+
+pub mod _match;
 pub(super) mod combine;
 mod generalize;
 mod glb;
@@ -8,15 +16,3 @@ mod higher_ranked;
 mod lattice;
 mod lub;
 mod type_relating;
-
-/// Whether aliases should be related structurally or not. Used
-/// to adjust the behavior of generalization and combine.
-///
-/// This should always be `No` unless in a few special-cases when
-/// instantiating canonical responses and in the new solver. Each
-/// such case should have a comment explaining why it is used.
-#[derive(Debug, Copy, Clone)]
-pub enum StructurallyRelateAliases {
-    Yes,
-    No,
-}
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
index e55a5878821..fd0bc9f44f7 100644
--- a/compiler/rustc_infer/src/infer/relate/type_relating.rs
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -1,12 +1,11 @@
 use super::combine::CombineFields;
 use crate::infer::BoundRegionConversionTime::HigherRankedType;
-use crate::infer::{
-    DefineOpaqueTypes, ObligationEmittingRelation, StructurallyRelateAliases, SubregionOrigin,
-};
+use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
 use crate::traits::{Obligation, PredicateObligations};
 
-use rustc_middle::ty::relate::{
-    relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation,
+use super::{
+    relate_args_invariantly, relate_args_with_variances, ObligationEmittingRelation, Relate,
+    RelateResult, StructurallyRelateAliases, TypeRelation,
 };
 use rustc_middle::ty::TyVar;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -29,7 +28,7 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> {
     }
 }
 
-impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
     fn tag(&self) -> &'static str {
         "TypeRelating"
     }
@@ -56,10 +55,10 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
         }
     }
 
-    fn relate_with_variance<T: Relate<'tcx>>(
+    fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
         &mut self,
         variance: ty::Variance,
-        _info: ty::VarianceDiagInfo<'tcx>,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
         a: T,
         b: T,
     ) -> RelateResult<'tcx, T> {
@@ -226,7 +225,7 @@ impl<'tcx> TypeRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
         b: ty::Binder<'tcx, T>,
     ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
     where
-        T: Relate<'tcx>,
+        T: Relate<TyCtxt<'tcx>>,
     {
         if a == b {
             // Do nothing
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 87c433a5dc0..8c9abeafacf 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -51,7 +51,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_hir::intravisit::FnKind as HirFnKind;
-use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin};
+use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin};
 use rustc_middle::bug;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::layout::LayoutOf;
@@ -1423,11 +1423,20 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
         self.perform_lint(cx, "item", foreign_item.owner_id.def_id, foreign_item.vis_span, true);
     }
 
-    fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
-        if matches!(cx.tcx.parent_hir_node(field.hir_id), Node::Variant(_)) {
-            return;
-        }
-        self.perform_lint(cx, "field", field.def_id, field.vis_span, false);
+    fn check_field_def(&mut self, _cx: &LateContext<'_>, _field: &hir::FieldDef<'_>) {
+        // - If an ADT definition is reported then we don't need to check fields
+        //   (as it would add unnecessary complexity to the source code, the struct
+        //   definition is in the immediate proximity to give the "real" visibility).
+        // - If an ADT is not reported because it's not `pub` - we don't need to
+        //   check fields.
+        // - If an ADT is not reported because it's reachable - we also don't need
+        //   to check fields because then they are reachable by construction if they
+        //   are pub.
+        //
+        // Therefore in no case we check the fields.
+        //
+        // cf. https://github.com/rust-lang/rust/pull/126013#issuecomment-2152839205
+        // cf. https://github.com/rust-lang/rust/pull/126040#issuecomment-2152944506
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index 44a3e9760e1..ad283117d7e 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -581,7 +581,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
                 self.tcx.crate_types().iter().all(|c| *c == CrateType::Rlib),
                 hash,
                 extra_filename,
-                false, // is_host
                 path_kind,
             );
 
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 73443de3553..90fe52a3438 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -222,7 +222,6 @@ use rustc_data_structures::owned_slice::slice_owned;
 use rustc_data_structures::svh::Svh;
 use rustc_errors::{DiagArgValue, IntoDiagArg};
 use rustc_fs_util::try_canonicalize;
-use rustc_session::config;
 use rustc_session::cstore::CrateSource;
 use rustc_session::filesearch::FileSearch;
 use rustc_session::search_paths::PathKind;
@@ -309,7 +308,6 @@ impl<'a> CrateLocator<'a> {
         is_rlib: bool,
         hash: Option<Svh>,
         extra_filename: Option<&'a str>,
-        is_host: bool,
         path_kind: PathKind,
     ) -> CrateLocator<'a> {
         let needs_object_code = sess.opts.output_types.should_codegen();
@@ -340,17 +338,9 @@ impl<'a> CrateLocator<'a> {
             },
             hash,
             extra_filename,
-            target: if is_host { &sess.host } else { &sess.target },
-            triple: if is_host {
-                TargetTriple::from_triple(config::host_triple())
-            } else {
-                sess.opts.target_triple.clone()
-            },
-            filesearch: if is_host {
-                sess.host_filesearch(path_kind)
-            } else {
-                sess.target_filesearch(path_kind)
-            },
+            target: &sess.target,
+            triple: sess.opts.target_triple.clone(),
+            filesearch: sess.target_filesearch(path_kind),
             is_proc_macro: false,
             crate_rejections: CrateRejections::default(),
         }
@@ -424,12 +414,18 @@ impl<'a> CrateLocator<'a> {
                 debug!("testing {}", spf.path.display());
 
                 let f = &spf.file_name_str;
-                let (hash, kind) = if f.starts_with(rlib_prefix) && f.ends_with(rlib_suffix) {
-                    (&f[rlib_prefix.len()..(f.len() - rlib_suffix.len())], CrateFlavor::Rlib)
-                } else if f.starts_with(rmeta_prefix) && f.ends_with(rmeta_suffix) {
-                    (&f[rmeta_prefix.len()..(f.len() - rmeta_suffix.len())], CrateFlavor::Rmeta)
-                } else if f.starts_with(dylib_prefix) && f.ends_with(dylib_suffix.as_ref()) {
-                    (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib)
+                let (hash, kind) = if let Some(f) = f.strip_prefix(rlib_prefix)
+                    && let Some(f) = f.strip_suffix(rlib_suffix)
+                {
+                    (f, CrateFlavor::Rlib)
+                } else if let Some(f) = f.strip_prefix(rmeta_prefix)
+                    && let Some(f) = f.strip_suffix(rmeta_suffix)
+                {
+                    (f, CrateFlavor::Rmeta)
+                } else if let Some(f) = f.strip_prefix(dylib_prefix)
+                    && let Some(f) = f.strip_suffix(dylib_suffix.as_ref())
+                {
+                    (f, CrateFlavor::Dylib)
                 } else {
                     if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix.as_ref()) {
                         self.crate_rejections.via_kind.push(CrateMismatch {
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index ab0c598ea0c..d1cdabc293d 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -28,7 +28,6 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
-rustc_next_trait_solver = { path = "../rustc_next_trait_solver" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index bf6ab800064..de786c38326 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -62,7 +62,7 @@ macro_rules! arena_types {
             [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
             [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
             [] canonical_goal_evaluation:
-                rustc_next_trait_solver::solve::inspect::CanonicalGoalEvaluationStep<
+                rustc_type_ir::solve::inspect::CanonicalGoalEvaluationStep<
                     rustc_middle::ty::TyCtxt<'tcx>
                 >,
             [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 8a3bbf71eb6..305ba1ef3bb 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -1,5 +1,3 @@
-use std::borrow::Cow;
-
 use crate::hir::ModuleItems;
 use crate::middle::debugger_visualizer::DebuggerVisualizerFile;
 use crate::query::LocalCrate;
@@ -256,26 +254,13 @@ impl<'hir> Map<'hir> {
 
     /// Given a `LocalDefId`, returns the `BodyId` associated with it,
     /// if the node is a body owner, otherwise returns `None`.
-    pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<Cow<'hir, Body<'hir>>> {
-        Some(match self.tcx.def_kind(id) {
-            // Inline consts do not have bodies of their own, so create one to make the follow-up logic simpler.
-            DefKind::InlineConst => {
-                let e = self.expect_expr(self.tcx.local_def_id_to_hir_id(id));
-                Cow::Owned(Body {
-                    params: &[],
-                    value: match e.kind {
-                        ExprKind::ConstBlock(body) => body,
-                        _ => span_bug!(e.span, "InlineConst was not a ConstBlock: {e:#?}"),
-                    },
-                })
-            }
-            _ => Cow::Borrowed(self.body(self.tcx.hir_node_by_def_id(id).body_id()?)),
-        })
+    pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<&'hir Body<'hir>> {
+        Some(self.body(self.tcx.hir_node_by_def_id(id).body_id()?))
     }
 
     /// Given a body owner's id, returns the `BodyId` associated with it.
     #[track_caller]
-    pub fn body_owned_by(self, id: LocalDefId) -> Cow<'hir, Body<'hir>> {
+    pub fn body_owned_by(self, id: LocalDefId) -> &'hir Body<'hir> {
         self.maybe_body_owned_by(id).unwrap_or_else(|| {
             let hir_id = self.tcx.local_def_id_to_hir_id(id);
             span_bug!(
@@ -338,7 +323,7 @@ impl<'hir> Map<'hir> {
 
     /// Returns an iterator of the `DefId`s for all body-owners in this
     /// crate. If you would prefer to iterate over the bodies
-    /// themselves, you can do `self.hir().krate().owners.iter()`.
+    /// themselves, you can do `self.hir().krate().body_ids.iter()`.
     #[inline]
     pub fn body_owners(self) -> impl Iterator<Item = LocalDefId> + 'hir {
         self.tcx.hir_crate_items(()).body_owners.iter().copied()
@@ -525,17 +510,7 @@ impl<'hir> Map<'hir> {
     /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context.
     /// Used exclusively for diagnostics, to avoid suggestion function calls.
     pub fn is_inside_const_context(self, hir_id: HirId) -> bool {
-        for (_, node) in self.parent_iter(hir_id) {
-            if let Some((def_id, _)) = node.associated_body() {
-                return self.body_const_context(def_id).is_some();
-            }
-            if let Node::Expr(e) = node {
-                if let ExprKind::ConstBlock(_) = e.kind {
-                    return true;
-                }
-            }
-        }
-        false
+        self.body_const_context(self.enclosing_body_owner(hir_id)).is_some()
     }
 
     /// Retrieves the `HirId` for `id`'s enclosing function *if* the `id` block or return is
@@ -918,6 +893,7 @@ impl<'hir> Map<'hir> {
             Node::Variant(variant) => variant.span,
             Node::Field(field) => field.span,
             Node::AnonConst(constant) => constant.span,
+            Node::ConstBlock(constant) => self.body(constant.body).value.span,
             Node::Expr(expr) => expr.span,
             Node::ExprField(field) => field.span,
             Node::Stmt(stmt) => stmt.span,
@@ -1187,6 +1163,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
             format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id))
         }
         Node::AnonConst(_) => node_str("const"),
+        Node::ConstBlock(_) => node_str("const"),
         Node::Expr(_) => node_str("expr"),
         Node::ExprField(_) => node_str("expr field"),
         Node::Stmt(_) => node_str("stmt"),
@@ -1336,6 +1313,11 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
         intravisit::walk_anon_const(self, c)
     }
 
+    fn visit_inline_const(&mut self, c: &'hir ConstBlock) {
+        self.body_owners.push(c.def_id);
+        intravisit::walk_inline_const(self, c)
+    }
+
     fn visit_expr(&mut self, ex: &'hir Expr<'hir>) {
         if let ExprKind::Closure(closure) = ex.kind {
             self.body_owners.push(closure.def_id);
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index c5c87c506b7..85357265687 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -197,6 +197,11 @@ impl<Prov> Scalar<Prov> {
     }
 
     #[inline]
+    pub fn from_i128(i: i128) -> Self {
+        Self::from_int(i, Size::from_bits(128))
+    }
+
+    #[inline]
     pub fn from_target_isize(i: i64, cx: &impl HasDataLayout) -> Self {
         Self::from_int(i, cx.data_layout().pointer_size)
     }
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index a3d2140eb1b..3d79ec0092f 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -5,9 +5,9 @@ use rustc_data_structures::base_n::BaseNString;
 use rustc_data_structures::base_n::ToBaseN;
 use rustc_data_structures::base_n::CASE_INSENSITIVE;
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
+use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher, ToStableHashKey};
+use rustc_data_structures::unord::UnordMap;
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc_hir::ItemId;
 use rustc_index::Idx;
@@ -241,7 +241,17 @@ impl<'tcx> fmt::Display for MonoItem<'tcx> {
     }
 }
 
-#[derive(Debug)]
+impl ToStableHashKey<StableHashingContext<'_>> for MonoItem<'_> {
+    type KeyType = Fingerprint;
+
+    fn to_stable_hash_key(&self, hcx: &StableHashingContext<'_>) -> Self::KeyType {
+        let mut hasher = StableHasher::new();
+        self.hash_stable(&mut hcx.clone(), &mut hasher);
+        hasher.finish()
+    }
+}
+
+#[derive(Debug, HashStable)]
 pub struct CodegenUnit<'tcx> {
     /// A name for this CGU. Incremental compilation requires that
     /// name be unique amongst **all** crates. Therefore, it should
@@ -430,38 +440,19 @@ impl<'tcx> CodegenUnit<'tcx> {
     }
 }
 
-impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for CodegenUnit<'tcx> {
-    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
-        let CodegenUnit {
-            ref items,
-            name,
-            // The size estimate is not relevant to the hash
-            size_estimate: _,
-            primary: _,
-            is_code_coverage_dead_code_cgu,
-        } = *self;
-
-        name.hash_stable(hcx, hasher);
-        is_code_coverage_dead_code_cgu.hash_stable(hcx, hasher);
-
-        let mut items: Vec<(Fingerprint, _)> = items
-            .iter()
-            .map(|(mono_item, &attrs)| {
-                let mut hasher = StableHasher::new();
-                mono_item.hash_stable(hcx, &mut hasher);
-                let mono_item_fingerprint = hasher.finish();
-                (mono_item_fingerprint, attrs)
-            })
-            .collect();
-
-        items.sort_unstable_by_key(|i| i.0);
-        items.hash_stable(hcx, hasher);
+impl ToStableHashKey<StableHashingContext<'_>> for CodegenUnit<'_> {
+    type KeyType = String;
+
+    fn to_stable_hash_key(&self, _: &StableHashingContext<'_>) -> Self::KeyType {
+        // Codegen unit names are conceptually required to be stable across
+        // compilation session so that object file names match up.
+        self.name.to_string()
     }
 }
 
 pub struct CodegenUnitNameBuilder<'tcx> {
     tcx: TyCtxt<'tcx>,
-    cache: FxHashMap<CrateNum, String>,
+    cache: UnordMap<CrateNum, String>,
 }
 
 impl<'tcx> CodegenUnitNameBuilder<'tcx> {
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 63678ab659d..202d587f0ad 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -32,7 +32,7 @@ use std::hash::{Hash, Hasher};
 
 pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
 // FIXME: Remove this import and import via `solve::`
-pub use rustc_next_trait_solver::solve::BuiltinImplSource;
+pub use rustc_type_ir::solve::BuiltinImplSource;
 
 /// Depending on the stage of compilation, we want projection to be
 /// more or less conservative.
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 66e50307733..50b6c77e1b2 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -7,13 +7,12 @@
 
 use crate::error::DropCheckOverflow;
 use crate::infer::canonical::{Canonical, QueryResponse};
-use crate::ty::error::TypeError;
 use crate::ty::GenericArg;
 use crate::ty::{self, Ty, TyCtxt};
 use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
 use rustc_span::Span;
 // FIXME: Remove this import and import via `traits::solve`.
-pub use rustc_next_trait_solver::solve::NoSolution;
+pub use rustc_type_ir::solve::NoSolution;
 
 pub mod type_op {
     use crate::ty::fold::TypeFoldable;
@@ -91,12 +90,6 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> =
 pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
     Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
 
-impl<'tcx> From<TypeError<'tcx>> for NoSolution {
-    fn from(_: TypeError<'tcx>) -> NoSolution {
-        NoSolution
-    }
-}
-
 #[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)]
 pub struct DropckOutlivesResult<'tcx> {
     pub kinds: Vec<GenericArg<'tcx>>,
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index c8c16ec1e2c..0d9ce402c64 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -1,8 +1,8 @@
 use rustc_ast_ir::try_visit;
 use rustc_data_structures::intern::Interned;
 use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
-use rustc_next_trait_solver as ir;
-pub use rustc_next_trait_solver::solve::*;
+use rustc_type_ir as ir;
+pub use rustc_type_ir::solve::*;
 
 use crate::infer::canonical::QueryRegionConstraints;
 use crate::ty::{
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 5f9b870331c..886dbd317af 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -200,6 +200,12 @@ impl<'tcx> AdtDef<'tcx> {
     }
 }
 
+impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
+    fn def_id(self) -> DefId {
+        self.did()
+    }
+}
+
 #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)]
 pub enum AdtKind {
     Struct,
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index dc13cc5437d..cc1daeb6419 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -149,6 +149,10 @@ impl<'tcx> Const<'tcx> {
 }
 
 impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
+    fn try_to_target_usize(self, interner: TyCtxt<'tcx>) -> Option<u64> {
+        self.try_to_target_usize(interner)
+    }
+
     fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst) -> Self {
         Const::new_infer(tcx, infer)
     }
@@ -168,6 +172,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
     fn new_unevaluated(interner: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Self {
         Const::new_unevaluated(interner, uv)
     }
+
+    fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self {
+        Const::new_expr(interner, expr)
+    }
 }
 
 impl<'tcx> Const<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 6b35b1f2d13..65d744239a6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -69,6 +69,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
 use rustc_target::spec::abi;
+use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::WithCachedTypeInfo;
 use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};
@@ -135,9 +136,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type ParamEnv = ty::ParamEnv<'tcx>;
     type Predicate = Predicate<'tcx>;
     type Clause = Clause<'tcx>;
-
     type Clauses = ty::Clauses<'tcx>;
 
+    fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
+        self.expand_abstract_consts(t)
+    }
+
     fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
         self.mk_canonical_var_infos(infos)
     }
@@ -148,6 +152,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.generics_of(def_id)
     }
 
+    type VariancesOf = &'tcx [ty::Variance];
+
+    fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
+        self.variances_of(def_id)
+    }
+
     fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
         self.type_of(def_id)
     }
@@ -205,7 +215,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.mk_args(args)
     }
 
-    fn mk_args_from_iter(self, args: impl Iterator<Item = Self::GenericArg>) -> Self::GenericArgs {
+    fn mk_args_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::GenericArg, Self::GenericArgs>,
+    {
         self.mk_args_from_iter(args)
     }
 
@@ -224,6 +238,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.arena.alloc(step)
     }
 
+    fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::Ty, Self::Tys>,
+    {
+        self.mk_type_list_from_iter(args)
+    }
+
     fn parent(self, def_id: Self::DefId) -> Self::DefId {
         self.parent(def_id)
     }
@@ -231,6 +253,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn recursion_limit(self) -> usize {
         self.recursion_limit().0
     }
+
+    type Features = &'tcx rustc_feature::Features;
+
+    fn features(self) -> Self::Features {
+        self.features()
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for abi::Abi {
@@ -249,6 +277,12 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
     }
 }
 
+impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_feature::Features {
+    fn generic_const_exprs(self) -> bool {
+        self.generic_const_exprs
+    }
+}
+
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
 
 pub struct CtxtInterners<'tcx> {
@@ -740,7 +774,6 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> {
                 1,
             ),
             bodies,
-            has_inline_consts: false,
         })));
         self.feed_owner_id().hir_attrs(attrs);
     }
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 9e2c626478a..32dc9fa5fc6 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -1,89 +1,26 @@
 use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter};
-use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
+use crate::ty::{self, Ty, TyCtxt};
+
 use rustc_errors::pluralize;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
-use rustc_hir::def_id::DefId;
-use rustc_macros::{TypeFoldable, TypeVisitable};
-use rustc_span::symbol::Symbol;
-use rustc_target::spec::abi;
+use rustc_macros::extension;
+pub use rustc_type_ir::error::ExpectedFound;
+
 use std::borrow::Cow;
 use std::hash::{DefaultHasher, Hash, Hasher};
 use std::path::PathBuf;
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)]
-pub struct ExpectedFound<T> {
-    pub expected: T,
-    pub found: T,
-}
-
-impl<T> ExpectedFound<T> {
-    pub fn new(a_is_expected: bool, a: T, b: T) -> Self {
-        if a_is_expected {
-            ExpectedFound { expected: a, found: b }
-        } else {
-            ExpectedFound { expected: b, found: a }
-        }
-    }
-}
-
-// Data structures used in type unification
-#[derive(Copy, Clone, Debug, TypeVisitable, PartialEq, Eq)]
-#[rustc_pass_by_value]
-pub enum TypeError<'tcx> {
-    Mismatch,
-    ConstnessMismatch(ExpectedFound<ty::BoundConstness>),
-    PolarityMismatch(ExpectedFound<ty::PredicatePolarity>),
-    SafetyMismatch(ExpectedFound<hir::Safety>),
-    AbiMismatch(ExpectedFound<abi::Abi>),
-    Mutability,
-    ArgumentMutability(usize),
-    TupleSize(ExpectedFound<usize>),
-    FixedArraySize(ExpectedFound<u64>),
-    ArgCount,
-    FieldMisMatch(Symbol, Symbol),
-
-    RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
-    RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
-    RegionsPlaceholderMismatch,
-
-    Sorts(ExpectedFound<Ty<'tcx>>),
-    ArgumentSorts(ExpectedFound<Ty<'tcx>>, usize),
-    Traits(ExpectedFound<DefId>),
-    VariadicMismatch(ExpectedFound<bool>),
-
-    /// Instantiating a type variable with the given type would have
-    /// created a cycle (because it appears somewhere within that
-    /// type).
-    CyclicTy(Ty<'tcx>),
-    CyclicConst(ty::Const<'tcx>),
-    ProjectionMismatched(ExpectedFound<DefId>),
-    ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>),
-    ConstMismatch(ExpectedFound<ty::Const<'tcx>>),
-
-    IntrinsicCast,
-    /// Safe `#[target_feature]` functions are not assignable to safe function pointers.
-    TargetFeatureCast(DefId),
-}
-
-impl TypeError<'_> {
-    pub fn involves_regions(self) -> bool {
-        match self {
-            TypeError::RegionsDoesNotOutlive(_, _)
-            | TypeError::RegionsInsufficientlyPolymorphic(_, _)
-            | TypeError::RegionsPlaceholderMismatch => true,
-            _ => false,
-        }
-    }
-}
+pub type TypeError<'tcx> = rustc_type_ir::error::TypeError<TyCtxt<'tcx>>;
 
-/// Explains the source of a type err in a short, human readable way. This is meant to be placed
-/// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
-/// afterwards to present additional details, particularly when it comes to lifetime-related
-/// errors.
+/// Explains the source of a type err in a short, human readable way.
+/// This is meant to be placed in parentheses after some larger message.
+/// You should also invoke `note_and_explain_type_err()` afterwards
+/// to present additional details, particularly when it comes to lifetime-
+/// related errors.
+#[extension(pub trait TypeErrorToStringExt<'tcx>)]
 impl<'tcx> TypeError<'tcx> {
-    pub fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
-        use self::TypeError::*;
+    fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
         fn report_maybe_different(expected: &str, found: &str) -> String {
             // A naive approach to making sure that we're not reporting silly errors such as:
             // (expected closure, found closure).
@@ -95,24 +32,26 @@ impl<'tcx> TypeError<'tcx> {
         }
 
         match self {
-            CyclicTy(_) => "cyclic type of infinite size".into(),
-            CyclicConst(_) => "encountered a self-referencing constant".into(),
-            Mismatch => "types differ".into(),
-            ConstnessMismatch(values) => {
+            TypeError::CyclicTy(_) => "cyclic type of infinite size".into(),
+            TypeError::CyclicConst(_) => "encountered a self-referencing constant".into(),
+            TypeError::Mismatch => "types differ".into(),
+            TypeError::ConstnessMismatch(values) => {
                 format!("expected {} bound, found {} bound", values.expected, values.found).into()
             }
-            PolarityMismatch(values) => {
+            TypeError::PolarityMismatch(values) => {
                 format!("expected {} polarity, found {} polarity", values.expected, values.found)
                     .into()
             }
-            SafetyMismatch(values) => {
+            TypeError::SafetyMismatch(values) => {
                 format!("expected {} fn, found {} fn", values.expected, values.found).into()
             }
-            AbiMismatch(values) => {
+            TypeError::AbiMismatch(values) => {
                 format!("expected {} fn, found {} fn", values.expected, values.found).into()
             }
-            ArgumentMutability(_) | Mutability => "types differ in mutability".into(),
-            TupleSize(values) => format!(
+            TypeError::ArgumentMutability(_) | TypeError::Mutability => {
+                "types differ in mutability".into()
+            }
+            TypeError::TupleSize(values) => format!(
                 "expected a tuple with {} element{}, found one with {} element{}",
                 values.expected,
                 pluralize!(values.expected),
@@ -120,7 +59,7 @@ impl<'tcx> TypeError<'tcx> {
                 pluralize!(values.found)
             )
             .into(),
-            FixedArraySize(values) => format!(
+            TypeError::FixedArraySize(values) => format!(
                 "expected an array with a fixed size of {} element{}, found one with {} element{}",
                 values.expected,
                 pluralize!(values.expected),
@@ -128,20 +67,21 @@ impl<'tcx> TypeError<'tcx> {
                 pluralize!(values.found)
             )
             .into(),
-            ArgCount => "incorrect number of function parameters".into(),
-            FieldMisMatch(adt, field) => format!("field type mismatch: {adt}.{field}").into(),
-            RegionsDoesNotOutlive(..) => "lifetime mismatch".into(),
+            TypeError::ArgCount => "incorrect number of function parameters".into(),
+            TypeError::RegionsDoesNotOutlive(..) => "lifetime mismatch".into(),
             // Actually naming the region here is a bit confusing because context is lacking
-            RegionsInsufficientlyPolymorphic(..) => {
+            TypeError::RegionsInsufficientlyPolymorphic(..) => {
+                "one type is more general than the other".into()
+            }
+            TypeError::RegionsPlaceholderMismatch => {
                 "one type is more general than the other".into()
             }
-            RegionsPlaceholderMismatch => "one type is more general than the other".into(),
-            ArgumentSorts(values, _) | Sorts(values) => {
+            TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
                 let expected = values.expected.sort_string(tcx);
                 let found = values.found.sort_string(tcx);
                 report_maybe_different(&expected, &found).into()
             }
-            Traits(values) => {
+            TypeError::Traits(values) => {
                 let (mut expected, mut found) = with_forced_trimmed_paths!((
                     tcx.def_path_str(values.expected),
                     tcx.def_path_str(values.found),
@@ -153,59 +93,34 @@ impl<'tcx> TypeError<'tcx> {
                 report_maybe_different(&format!("trait `{expected}`"), &format!("trait `{found}`"))
                     .into()
             }
-            VariadicMismatch(ref values) => format!(
+            TypeError::VariadicMismatch(ref values) => format!(
                 "expected {} fn, found {} function",
                 if values.expected { "variadic" } else { "non-variadic" },
                 if values.found { "variadic" } else { "non-variadic" }
             )
             .into(),
-            ProjectionMismatched(ref values) => format!(
+            TypeError::ProjectionMismatched(ref values) => format!(
                 "expected `{}`, found `{}`",
                 tcx.def_path_str(values.expected),
                 tcx.def_path_str(values.found)
             )
             .into(),
-            ExistentialMismatch(ref values) => report_maybe_different(
+            TypeError::ExistentialMismatch(ref values) => report_maybe_different(
                 &format!("trait `{}`", values.expected),
                 &format!("trait `{}`", values.found),
             )
             .into(),
-            ConstMismatch(ref values) => {
+            TypeError::ConstMismatch(ref values) => {
                 format!("expected `{}`, found `{}`", values.expected, values.found).into()
             }
-            IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
-            TargetFeatureCast(_) => {
+            TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
+            TypeError::TargetFeatureCast(_) => {
                 "cannot coerce functions with `#[target_feature]` to safe function pointers".into()
             }
         }
     }
 }
 
-impl<'tcx> TypeError<'tcx> {
-    pub fn must_include_note(self) -> bool {
-        use self::TypeError::*;
-        match self {
-            CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_)
-            | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
-            | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false,
-
-            Mutability
-            | ArgumentMutability(_)
-            | TupleSize(_)
-            | ArgCount
-            | FieldMisMatch(..)
-            | RegionsDoesNotOutlive(..)
-            | RegionsInsufficientlyPolymorphic(..)
-            | RegionsPlaceholderMismatch
-            | Traits(_)
-            | ProjectionMismatched(_)
-            | ExistentialMismatch(_)
-            | ConstMismatch(_)
-            | IntrinsicCast => true,
-        }
-    }
-}
-
 impl<'tcx> Ty<'tcx> {
     pub fn sort_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
         match *self.kind() {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index e0fbf127e70..7ff1b799822 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -60,6 +60,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{ExpnId, ExpnKind, Span};
 use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
 pub use rustc_target::abi::{ReprFlags, ReprOptions};
+pub use rustc_type_ir::relate::VarianceDiagInfo;
 pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx};
 use tracing::{debug, instrument};
 pub use vtable::*;
@@ -114,7 +115,7 @@ pub use self::rvalue_scopes::RvalueScopes;
 pub use self::sty::{
     AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
     CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst,
-    ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo,
+    ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs,
 };
 pub use self::trait_def::TraitDef;
 pub use self::typeck_results::{
@@ -122,7 +123,6 @@ pub use self::typeck_results::{
     TypeckResults, UserType, UserTypeAnnotationIndex,
 };
 
-pub mod _match;
 pub mod abstract_const;
 pub mod adjustment;
 pub mod cast;
@@ -313,38 +313,6 @@ impl Visibility {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
-pub enum BoundConstness {
-    /// `Type: Trait`
-    NotConst,
-    /// `Type: const Trait`
-    Const,
-    /// `Type: ~const Trait`
-    ///
-    /// Requires resolving to const only when we are in a const context.
-    ConstIfConst,
-}
-
-impl BoundConstness {
-    pub fn as_str(self) -> &'static str {
-        match self {
-            Self::NotConst => "",
-            Self::Const => "const",
-            Self::ConstIfConst => "~const",
-        }
-    }
-}
-
-impl fmt::Display for BoundConstness {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::NotConst => f.write_str("normal"),
-            Self::Const => f.write_str("const"),
-            Self::ConstIfConst => f.write_str("~const"),
-        }
-    }
-}
-
 #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)]
 #[derive(TypeFoldable, TypeVisitable)]
 pub struct ClosureSizeProfileData<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index e24e64b2301..b169d672a84 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -1,383 +1,54 @@
-//! Generalized type relating mechanism.
-//!
-//! A type relation `R` relates a pair of values `(A, B)`. `A and B` are usually
-//! types or regions but can be other things. Examples of type relations are
-//! subtyping, type equality, etc.
+use std::iter;
 
-use crate::ty::error::{ExpectedFound, TypeError};
-use crate::ty::{
-    self, ExistentialPredicate, ExistentialPredicateStableCmpExt as _, GenericArg, GenericArgKind,
-    GenericArgsRef, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldable,
-};
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_macros::TypeVisitable;
 use rustc_target::spec::abi;
-use std::iter;
-use tracing::{debug, instrument};
-
-use super::Pattern;
-
-pub type RelateResult<'tcx, T> = Result<T, TypeError<'tcx>>;
-
-pub trait TypeRelation<'tcx>: Sized {
-    fn tcx(&self) -> TyCtxt<'tcx>;
-
-    /// Returns a static string we can use for printouts.
-    fn tag(&self) -> &'static str;
-
-    /// Generic relation routine suitable for most anything.
-    fn relate<T: Relate<'tcx>>(&mut self, a: T, b: T) -> RelateResult<'tcx, T> {
-        Relate::relate(self, a, b)
-    }
-
-    /// Relate the two args for the given item. The default
-    /// is to look up the variance for the item and proceed
-    /// accordingly.
-    fn relate_item_args(
-        &mut self,
-        item_def_id: DefId,
-        a_arg: GenericArgsRef<'tcx>,
-        b_arg: GenericArgsRef<'tcx>,
-    ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-        debug!(
-            "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})",
-            item_def_id, a_arg, b_arg
-        );
-
-        let tcx = self.tcx();
-        let opt_variances = tcx.variances_of(item_def_id);
-        relate_args_with_variances(self, item_def_id, opt_variances, a_arg, b_arg, true)
-    }
-
-    /// Switch variance for the purpose of relating `a` and `b`.
-    fn relate_with_variance<T: Relate<'tcx>>(
-        &mut self,
-        variance: ty::Variance,
-        info: ty::VarianceDiagInfo<'tcx>,
-        a: T,
-        b: T,
-    ) -> RelateResult<'tcx, T>;
-
-    // Overridable relations. You shouldn't typically call these
-    // directly, instead call `relate()`, which in turn calls
-    // these. This is both more uniform but also allows us to add
-    // additional hooks for other types in the future if needed
-    // without making older code, which called `relate`, obsolete.
-
-    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>;
-
-    fn regions(
-        &mut self,
-        a: ty::Region<'tcx>,
-        b: ty::Region<'tcx>,
-    ) -> RelateResult<'tcx, ty::Region<'tcx>>;
-
-    fn consts(
-        &mut self,
-        a: ty::Const<'tcx>,
-        b: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>>;
-
-    fn binders<T>(
-        &mut self,
-        a: ty::Binder<'tcx, T>,
-        b: ty::Binder<'tcx, T>,
-    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
-    where
-        T: Relate<'tcx>;
-}
-
-pub trait Relate<'tcx>: TypeFoldable<TyCtxt<'tcx>> + PartialEq + Copy {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: Self,
-        b: Self,
-    ) -> RelateResult<'tcx, Self>;
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Relate impls
-
-#[inline]
-pub fn relate_args_invariantly<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    a_arg: GenericArgsRef<'tcx>,
-    b_arg: GenericArgsRef<'tcx>,
-) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-    relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| {
-        relation.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)
-    }))
-}
-
-pub fn relate_args_with_variances<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    ty_def_id: DefId,
-    variances: &[ty::Variance],
-    a_arg: GenericArgsRef<'tcx>,
-    b_arg: GenericArgsRef<'tcx>,
-    fetch_ty_for_diag: bool,
-) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
-    let tcx = relation.tcx();
-
-    let mut cached_ty = None;
-    let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| {
-        let variance = variances[i];
-        let variance_info = if variance == ty::Invariant && fetch_ty_for_diag {
-            let ty =
-                *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, a_arg));
-            ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
-        } else {
-            ty::VarianceDiagInfo::default()
-        };
-        relation.relate_with_variance(variance, variance_info, a, b)
-    });
-
-    tcx.mk_args_from_iter(params)
-}
-
-impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::FnSig<'tcx>,
-        b: ty::FnSig<'tcx>,
-    ) -> RelateResult<'tcx, ty::FnSig<'tcx>> {
-        let tcx = relation.tcx();
-
-        if a.c_variadic != b.c_variadic {
-            return Err(TypeError::VariadicMismatch(expected_found(a.c_variadic, b.c_variadic)));
-        }
-        let safety = relation.relate(a.safety, b.safety)?;
-        let abi = relation.relate(a.abi, b.abi)?;
-
-        if a.inputs().len() != b.inputs().len() {
-            return Err(TypeError::ArgCount);
-        }
+pub use rustc_type_ir::relate::*;
 
-        let inputs_and_output = iter::zip(a.inputs(), b.inputs())
-            .map(|(&a, &b)| ((a, b), false))
-            .chain(iter::once(((a.output(), b.output()), true)))
-            .map(|((a, b), is_output)| {
-                if is_output {
-                    relation.relate(a, b)
-                } else {
-                    relation.relate_with_variance(
-                        ty::Contravariant,
-                        ty::VarianceDiagInfo::default(),
-                        a,
-                        b,
-                    )
-                }
-            })
-            .enumerate()
-            .map(|(i, r)| match r {
-                Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => {
-                    Err(TypeError::ArgumentSorts(exp_found, i))
-                }
-                Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => {
-                    Err(TypeError::ArgumentMutability(i))
-                }
-                r => r,
-            });
-        Ok(ty::FnSig {
-            inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?,
-            c_variadic: a.c_variadic,
-            safety,
-            abi,
-        })
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::BoundConstness {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: ty::BoundConstness,
-        b: ty::BoundConstness,
-    ) -> RelateResult<'tcx, ty::BoundConstness> {
-        if a != b { Err(TypeError::ConstnessMismatch(expected_found(a, b))) } else { Ok(a) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for hir::Safety {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: hir::Safety,
-        b: hir::Safety,
-    ) -> RelateResult<'tcx, hir::Safety> {
-        if a != b { Err(TypeError::SafetyMismatch(expected_found(a, b))) } else { Ok(a) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for abi::Abi {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: abi::Abi,
-        b: abi::Abi,
-    ) -> RelateResult<'tcx, abi::Abi> {
-        if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(expected_found(a, b))) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::AliasTy<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::AliasTy<'tcx>,
-        b: ty::AliasTy<'tcx>,
-    ) -> RelateResult<'tcx, ty::AliasTy<'tcx>> {
-        if a.def_id != b.def_id {
-            Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = match a.kind(relation.tcx()) {
-                ty::Opaque => relate_args_with_variances(
-                    relation,
-                    a.def_id,
-                    relation.tcx().variances_of(a.def_id),
-                    a.args,
-                    b.args,
-                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
-                )?,
-                ty::Projection | ty::Weak | ty::Inherent => {
-                    relate_args_invariantly(relation, a.args, b.args)?
-                }
-            };
-            Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args))
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::AliasTerm<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::AliasTerm<'tcx>,
-        b: ty::AliasTerm<'tcx>,
-    ) -> RelateResult<'tcx, ty::AliasTerm<'tcx>> {
-        if a.def_id != b.def_id {
-            Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = match a.kind(relation.tcx()) {
-                ty::AliasTermKind::OpaqueTy => relate_args_with_variances(
-                    relation,
-                    a.def_id,
-                    relation.tcx().variances_of(a.def_id),
-                    a.args,
-                    b.args,
-                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
-                )?,
-                ty::AliasTermKind::ProjectionTy
-                | ty::AliasTermKind::WeakTy
-                | ty::AliasTermKind::InherentTy
-                | ty::AliasTermKind::UnevaluatedConst
-                | ty::AliasTermKind::ProjectionConst => {
-                    relate_args_invariantly(relation, a.args, b.args)?
-                }
-            };
-            Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args))
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::ExistentialProjection<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::ExistentialProjection<'tcx>,
-        b: ty::ExistentialProjection<'tcx>,
-    ) -> RelateResult<'tcx, ty::ExistentialProjection<'tcx>> {
-        if a.def_id != b.def_id {
-            Err(TypeError::ProjectionMismatched(expected_found(a.def_id, b.def_id)))
-        } else {
-            let term = relation.relate_with_variance(
-                ty::Invariant,
-                ty::VarianceDiagInfo::default(),
-                a.term,
-                b.term,
-            )?;
-            let args = relation.relate_with_variance(
-                ty::Invariant,
-                ty::VarianceDiagInfo::default(),
-                a.args,
-                b.args,
-            )?;
-            Ok(ty::ExistentialProjection { def_id: a.def_id, args, term })
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::TraitRef<'tcx>,
-        b: ty::TraitRef<'tcx>,
-    ) -> RelateResult<'tcx, ty::TraitRef<'tcx>> {
-        // Different traits cannot be related.
-        if a.def_id != b.def_id {
-            Err(TypeError::Traits(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = relate_args_invariantly(relation, a.args, b.args)?;
-            Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args))
-        }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::ExistentialTraitRef<'tcx>,
-        b: ty::ExistentialTraitRef<'tcx>,
-    ) -> RelateResult<'tcx, ty::ExistentialTraitRef<'tcx>> {
-        // Different traits cannot be related.
-        if a.def_id != b.def_id {
-            Err(TypeError::Traits(expected_found(a.def_id, b.def_id)))
-        } else {
-            let args = relate_args_invariantly(relation, a.args, b.args)?;
-            Ok(ty::ExistentialTraitRef { def_id: a.def_id, args })
-        }
-    }
-}
+use crate::ty::error::{ExpectedFound, TypeError};
+use crate::ty::predicate::ExistentialPredicateStableCmpExt as _;
+use crate::ty::{self as ty, Ty, TyCtxt};
 
-#[derive(PartialEq, Copy, Debug, Clone, TypeFoldable, TypeVisitable)]
-struct CoroutineWitness<'tcx>(&'tcx ty::List<Ty<'tcx>>);
+pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>;
 
-impl<'tcx> Relate<'tcx> for CoroutineWitness<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: CoroutineWitness<'tcx>,
-        b: CoroutineWitness<'tcx>,
-    ) -> RelateResult<'tcx, CoroutineWitness<'tcx>> {
-        assert_eq!(a.0.len(), b.0.len());
-        let tcx = relation.tcx();
-        let types =
-            tcx.mk_type_list_from_iter(iter::zip(a.0, b.0).map(|(a, b)| relation.relate(a, b)))?;
-        Ok(CoroutineWitness(types))
-    }
+/// Whether aliases should be related structurally or not. Used
+/// to adjust the behavior of generalization and combine.
+///
+/// This should always be `No` unless in a few special-cases when
+/// instantiating canonical responses and in the new solver. Each
+/// such case should have a comment explaining why it is used.
+#[derive(Debug, Copy, Clone)]
+pub enum StructurallyRelateAliases {
+    Yes,
+    No,
 }
 
-impl<'tcx> Relate<'tcx> for ImplSubject<'tcx> {
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::ImplSubject<'tcx> {
     #[inline]
-    fn relate<R: TypeRelation<'tcx>>(
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
-        a: ImplSubject<'tcx>,
-        b: ImplSubject<'tcx>,
-    ) -> RelateResult<'tcx, ImplSubject<'tcx>> {
+        a: ty::ImplSubject<'tcx>,
+        b: ty::ImplSubject<'tcx>,
+    ) -> RelateResult<'tcx, ty::ImplSubject<'tcx>> {
         match (a, b) {
-            (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => {
+            (ty::ImplSubject::Trait(trait_ref_a), ty::ImplSubject::Trait(trait_ref_b)) => {
                 let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?;
-                Ok(ImplSubject::Trait(trait_ref))
+                Ok(ty::ImplSubject::Trait(trait_ref))
             }
-            (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => {
+            (ty::ImplSubject::Inherent(ty_a), ty::ImplSubject::Inherent(ty_b)) => {
                 let ty = Ty::relate(relation, ty_a, ty_b)?;
-                Ok(ImplSubject::Inherent(ty))
+                Ok(ty::ImplSubject::Inherent(ty))
             }
-            (ImplSubject::Trait(_), ImplSubject::Inherent(_))
-            | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => {
+            (ty::ImplSubject::Trait(_), ty::ImplSubject::Inherent(_))
+            | (ty::ImplSubject::Inherent(_), ty::ImplSubject::Trait(_)) => {
                 bug!("can not relate TraitRef and Ty");
             }
         }
     }
 }
 
-impl<'tcx> Relate<'tcx> for Ty<'tcx> {
+impl<'tcx> Relate<TyCtxt<'tcx>> for Ty<'tcx> {
     #[inline]
-    fn relate<R: TypeRelation<'tcx>>(
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Ty<'tcx>,
         b: Ty<'tcx>,
@@ -386,9 +57,9 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> {
     }
 }
 
-impl<'tcx> Relate<'tcx> for Pattern<'tcx> {
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Pattern<'tcx> {
     #[inline]
-    fn relate<R: TypeRelation<'tcx>>(
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Self,
         b: Self,
@@ -416,276 +87,8 @@ impl<'tcx> Relate<'tcx> for Pattern<'tcx> {
     }
 }
 
-/// Relates `a` and `b` structurally, calling the relation for all nested values.
-/// Any semantic equality, e.g. of projections, and inference variables have to be
-/// handled by the caller.
-#[instrument(level = "trace", skip(relation), ret)]
-pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    a: Ty<'tcx>,
-    b: Ty<'tcx>,
-) -> RelateResult<'tcx, Ty<'tcx>> {
-    let tcx = relation.tcx();
-    match (a.kind(), b.kind()) {
-        (&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
-            // The caller should handle these cases!
-            bug!("var types encountered in structurally_relate_tys")
-        }
-
-        (ty::Bound(..), _) | (_, ty::Bound(..)) => {
-            bug!("bound types encountered in structurally_relate_tys")
-        }
-
-        (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)),
-
-        (&ty::Never, _)
-        | (&ty::Char, _)
-        | (&ty::Bool, _)
-        | (&ty::Int(_), _)
-        | (&ty::Uint(_), _)
-        | (&ty::Float(_), _)
-        | (&ty::Str, _)
-            if a == b =>
-        {
-            Ok(a)
-        }
-
-        (ty::Param(a_p), ty::Param(b_p)) if a_p.index == b_p.index => {
-            debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name");
-            Ok(a)
-        }
-
-        (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a),
-
-        (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) if a_def == b_def => {
-            let args = relation.relate_item_args(a_def.did(), a_args, b_args)?;
-            Ok(Ty::new_adt(tcx, a_def, args))
-        }
-
-        (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)),
-
-        (&ty::Dynamic(a_obj, a_region, a_repr), &ty::Dynamic(b_obj, b_region, b_repr))
-            if a_repr == b_repr =>
-        {
-            Ok(Ty::new_dynamic(
-                tcx,
-                relation.relate(a_obj, b_obj)?,
-                relation.relate(a_region, b_region)?,
-                a_repr,
-            ))
-        }
-
-        (&ty::Coroutine(a_id, a_args), &ty::Coroutine(b_id, b_args)) if a_id == b_id => {
-            // All Coroutine types with the same id represent
-            // the (anonymous) type of the same coroutine expression. So
-            // all of their regions should be equated.
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_coroutine(tcx, a_id, args))
-        }
-
-        (&ty::CoroutineWitness(a_id, a_args), &ty::CoroutineWitness(b_id, b_args))
-            if a_id == b_id =>
-        {
-            // All CoroutineWitness types with the same id represent
-            // the (anonymous) type of the same coroutine expression. So
-            // all of their regions should be equated.
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_coroutine_witness(tcx, a_id, args))
-        }
-
-        (&ty::Closure(a_id, a_args), &ty::Closure(b_id, b_args)) if a_id == b_id => {
-            // All Closure types with the same id represent
-            // the (anonymous) type of the same closure expression. So
-            // all of their regions should be equated.
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_closure(tcx, a_id, args))
-        }
-
-        (&ty::CoroutineClosure(a_id, a_args), &ty::CoroutineClosure(b_id, b_args))
-            if a_id == b_id =>
-        {
-            let args = relate_args_invariantly(relation, a_args, b_args)?;
-            Ok(Ty::new_coroutine_closure(tcx, a_id, args))
-        }
-
-        (&ty::RawPtr(a_ty, a_mutbl), &ty::RawPtr(b_ty, b_mutbl)) => {
-            if a_mutbl != b_mutbl {
-                return Err(TypeError::Mutability);
-            }
-
-            let (variance, info) = match a_mutbl {
-                hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None),
-                hir::Mutability::Mut => {
-                    (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
-                }
-            };
-
-            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
-
-            Ok(Ty::new_ptr(tcx, ty, a_mutbl))
-        }
-
-        (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => {
-            if a_mutbl != b_mutbl {
-                return Err(TypeError::Mutability);
-            }
-
-            let (variance, info) = match a_mutbl {
-                hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None),
-                hir::Mutability::Mut => {
-                    (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
-                }
-            };
-
-            let r = relation.relate(a_r, b_r)?;
-            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
-
-            Ok(Ty::new_ref(tcx, r, ty, a_mutbl))
-        }
-
-        (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => {
-            let t = relation.relate(a_t, b_t)?;
-            match relation.relate(sz_a, sz_b) {
-                Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)),
-                Err(err) => {
-                    // Check whether the lengths are both concrete/known values,
-                    // but are unequal, for better diagnostics.
-                    let sz_a = sz_a.try_to_target_usize(tcx);
-                    let sz_b = sz_b.try_to_target_usize(tcx);
-
-                    match (sz_a, sz_b) {
-                        (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => {
-                            Err(TypeError::FixedArraySize(expected_found(sz_a_val, sz_b_val)))
-                        }
-                        _ => Err(err),
-                    }
-                }
-            }
-        }
-
-        (&ty::Slice(a_t), &ty::Slice(b_t)) => {
-            let t = relation.relate(a_t, b_t)?;
-            Ok(Ty::new_slice(tcx, t))
-        }
-
-        (&ty::Tuple(as_), &ty::Tuple(bs)) => {
-            if as_.len() == bs.len() {
-                Ok(Ty::new_tup_from_iter(
-                    tcx,
-                    iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)),
-                )?)
-            } else if !(as_.is_empty() || bs.is_empty()) {
-                Err(TypeError::TupleSize(expected_found(as_.len(), bs.len())))
-            } else {
-                Err(TypeError::Sorts(expected_found(a, b)))
-            }
-        }
-
-        (&ty::FnDef(a_def_id, a_args), &ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => {
-            let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
-            Ok(Ty::new_fn_def(tcx, a_def_id, args))
-        }
-
-        (&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => {
-            let fty = relation.relate(a_fty, b_fty)?;
-            Ok(Ty::new_fn_ptr(tcx, fty))
-        }
-
-        // Alias tend to mostly already be handled downstream due to normalization.
-        (&ty::Alias(a_kind, a_data), &ty::Alias(b_kind, b_data)) => {
-            let alias_ty = relation.relate(a_data, b_data)?;
-            assert_eq!(a_kind, b_kind);
-            Ok(Ty::new_alias(tcx, a_kind, alias_ty))
-        }
-
-        (&ty::Pat(a_ty, a_pat), &ty::Pat(b_ty, b_pat)) => {
-            let ty = relation.relate(a_ty, b_ty)?;
-            let pat = relation.relate(a_pat, b_pat)?;
-            Ok(Ty::new_pat(tcx, ty, pat))
-        }
-
-        _ => Err(TypeError::Sorts(expected_found(a, b))),
-    }
-}
-
-/// Relates `a` and `b` structurally, calling the relation for all nested values.
-/// Any semantic equality, e.g. of unevaluated consts, and inference variables have
-/// to be handled by the caller.
-///
-/// FIXME: This is not totally structual, which probably should be fixed.
-/// See the HACKs below.
-pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
-    relation: &mut R,
-    mut a: ty::Const<'tcx>,
-    mut b: ty::Const<'tcx>,
-) -> RelateResult<'tcx, ty::Const<'tcx>> {
-    debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
-    let tcx = relation.tcx();
-
-    if tcx.features().generic_const_exprs {
-        a = tcx.expand_abstract_consts(a);
-        b = tcx.expand_abstract_consts(b);
-    }
-
-    debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
-
-    // Currently, the values that can be unified are primitive types,
-    // and those that derive both `PartialEq` and `Eq`, corresponding
-    // to structural-match types.
-    let is_match = match (a.kind(), b.kind()) {
-        (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
-            // The caller should handle these cases!
-            bug!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b)
-        }
-
-        (ty::ConstKind::Error(_), _) => return Ok(a),
-        (_, ty::ConstKind::Error(_)) => return Ok(b),
-
-        (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index == b_p.index => {
-            debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name");
-            true
-        }
-        (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
-        (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val,
-
-        // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
-        // and is the better alternative to waiting until `generic_const_exprs` can
-        // be stabilized.
-        (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => {
-            if cfg!(debug_assertions) {
-                let a_ty = tcx.type_of(au.def).instantiate(tcx, au.args);
-                let b_ty = tcx.type_of(bu.def).instantiate(tcx, bu.args);
-                assert_eq!(a_ty, b_ty);
-            }
-
-            let args = relation.relate_with_variance(
-                ty::Variance::Invariant,
-                ty::VarianceDiagInfo::default(),
-                au.args,
-                bu.args,
-            )?;
-            return Ok(ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst { def: au.def, args }));
-        }
-        (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => {
-            match (ae.kind, be.kind) {
-                (ty::ExprKind::Binop(a_binop), ty::ExprKind::Binop(b_binop))
-                    if a_binop == b_binop => {}
-                (ty::ExprKind::UnOp(a_unop), ty::ExprKind::UnOp(b_unop)) if a_unop == b_unop => {}
-                (ty::ExprKind::FunctionCall, ty::ExprKind::FunctionCall) => {}
-                (ty::ExprKind::Cast(a_kind), ty::ExprKind::Cast(b_kind)) if a_kind == b_kind => {}
-                _ => return Err(TypeError::ConstMismatch(expected_found(a, b))),
-            }
-
-            let args = relation.relate(ae.args(), be.args())?;
-            return Ok(ty::Const::new_expr(tcx, ty::Expr::new(ae.kind, args)));
-        }
-        _ => false,
-    };
-    if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(expected_found(a, b))) }
-}
-
-impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Self,
         b: Self,
@@ -703,44 +106,65 @@ impl<'tcx> Relate<'tcx> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
         b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
         b_v.dedup();
         if a_v.len() != b_v.len() {
-            return Err(TypeError::ExistentialMismatch(expected_found(a, b)));
+            return Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b)));
         }
 
         let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| {
             match (ep_a.skip_binder(), ep_b.skip_binder()) {
-                (ExistentialPredicate::Trait(a), ExistentialPredicate::Trait(b)) => Ok(ep_a
-                    .rebind(ExistentialPredicate::Trait(
-                        relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
-                    ))),
-                (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => {
-                    Ok(ep_a.rebind(ExistentialPredicate::Projection(
+                (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => {
+                    Ok(ep_a.rebind(ty::ExistentialPredicate::Trait(
                         relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
                     )))
                 }
-                (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b))
-                    if a == b =>
-                {
-                    Ok(ep_a.rebind(ExistentialPredicate::AutoTrait(a)))
-                }
-                _ => Err(TypeError::ExistentialMismatch(expected_found(a, b))),
+                (
+                    ty::ExistentialPredicate::Projection(a),
+                    ty::ExistentialPredicate::Projection(b),
+                ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection(
+                    relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
+                ))),
+                (
+                    ty::ExistentialPredicate::AutoTrait(a),
+                    ty::ExistentialPredicate::AutoTrait(b),
+                ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))),
+                _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(true, a, b))),
             }
         });
         tcx.mk_poly_existential_predicates_from_iter(v)
     }
 }
 
-impl<'tcx> Relate<'tcx> for GenericArgsRef<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for hir::Safety {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
+        _relation: &mut R,
+        a: hir::Safety,
+        b: hir::Safety,
+    ) -> RelateResult<'tcx, hir::Safety> {
+        if a != b { Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a, b))) } else { Ok(a) }
+    }
+}
+
+impl<'tcx> Relate<TyCtxt<'tcx>> for abi::Abi {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
+        _relation: &mut R,
+        a: abi::Abi,
+        b: abi::Abi,
+    ) -> RelateResult<'tcx, abi::Abi> {
+        if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(ExpectedFound::new(true, a, b))) }
+    }
+}
+
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
-        a: GenericArgsRef<'tcx>,
-        b: GenericArgsRef<'tcx>,
-    ) -> RelateResult<'tcx, GenericArgsRef<'tcx>> {
+        a: ty::GenericArgsRef<'tcx>,
+        b: ty::GenericArgsRef<'tcx>,
+    ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> {
         relate_args_invariantly(relation, a, b)
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::Region<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Region<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: ty::Region<'tcx>,
         b: ty::Region<'tcx>,
@@ -749,8 +173,8 @@ impl<'tcx> Relate<'tcx> for ty::Region<'tcx> {
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::Const<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Const<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: ty::Const<'tcx>,
         b: ty::Const<'tcx>,
@@ -759,85 +183,70 @@ impl<'tcx> Relate<'tcx> for ty::Const<'tcx> {
     }
 }
 
-impl<'tcx, T: Relate<'tcx>> Relate<'tcx> for ty::Binder<'tcx, T> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::Binder<'tcx, T>,
-        b: ty::Binder<'tcx, T>,
-    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> {
-        relation.binders(a, b)
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Expr<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
+        relation: &mut R,
+        ae: ty::Expr<'tcx>,
+        be: ty::Expr<'tcx>,
+    ) -> RelateResult<'tcx, ty::Expr<'tcx>> {
+        // FIXME(generic_const_exprs): is it possible to relate two consts which are not identical
+        // exprs? Should we care about that?
+        // FIXME(generic_const_exprs): relating the `ty()`s is a little weird since it is supposed to
+        // ICE If they mismatch. Unfortunately `ConstKind::Expr` is a little special and can be thought
+        // of as being generic over the argument types, however this is implicit so these types don't get
+        // related when we relate the args of the item this const arg is for.
+        match (ae.kind, be.kind) {
+            (ty::ExprKind::Binop(a_binop), ty::ExprKind::Binop(b_binop)) if a_binop == b_binop => {}
+            (ty::ExprKind::UnOp(a_unop), ty::ExprKind::UnOp(b_unop)) if a_unop == b_unop => {}
+            (ty::ExprKind::FunctionCall, ty::ExprKind::FunctionCall) => {}
+            (ty::ExprKind::Cast(a_kind), ty::ExprKind::Cast(b_kind)) if a_kind == b_kind => {}
+            _ => return Err(TypeError::Mismatch),
+        }
+
+        let args = relation.relate(ae.args(), be.args())?;
+        Ok(ty::Expr::new(ae.kind, args))
     }
 }
 
-impl<'tcx> Relate<'tcx> for GenericArg<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::GenericArg<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
-        a: GenericArg<'tcx>,
-        b: GenericArg<'tcx>,
-    ) -> RelateResult<'tcx, GenericArg<'tcx>> {
+        a: ty::GenericArg<'tcx>,
+        b: ty::GenericArg<'tcx>,
+    ) -> RelateResult<'tcx, ty::GenericArg<'tcx>> {
         match (a.unpack(), b.unpack()) {
-            (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => {
+            (ty::GenericArgKind::Lifetime(a_lt), ty::GenericArgKind::Lifetime(b_lt)) => {
                 Ok(relation.relate(a_lt, b_lt)?.into())
             }
-            (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => {
+            (ty::GenericArgKind::Type(a_ty), ty::GenericArgKind::Type(b_ty)) => {
                 Ok(relation.relate(a_ty, b_ty)?.into())
             }
-            (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => {
+            (ty::GenericArgKind::Const(a_ct), ty::GenericArgKind::Const(b_ct)) => {
                 Ok(relation.relate(a_ct, b_ct)?.into())
             }
-            (GenericArgKind::Lifetime(unpacked), x) => {
+            (ty::GenericArgKind::Lifetime(unpacked), x) => {
                 bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
             }
-            (GenericArgKind::Type(unpacked), x) => {
+            (ty::GenericArgKind::Type(unpacked), x) => {
                 bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
             }
-            (GenericArgKind::Const(unpacked), x) => {
+            (ty::GenericArgKind::Const(unpacked), x) => {
                 bug!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x)
             }
         }
     }
 }
 
-impl<'tcx> Relate<'tcx> for ty::PredicatePolarity {
-    fn relate<R: TypeRelation<'tcx>>(
-        _relation: &mut R,
-        a: ty::PredicatePolarity,
-        b: ty::PredicatePolarity,
-    ) -> RelateResult<'tcx, ty::PredicatePolarity> {
-        if a != b { Err(TypeError::PolarityMismatch(expected_found(a, b))) } else { Ok(a) }
-    }
-}
-
-impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
-        relation: &mut R,
-        a: ty::TraitPredicate<'tcx>,
-        b: ty::TraitPredicate<'tcx>,
-    ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> {
-        Ok(ty::TraitPredicate {
-            trait_ref: relation.relate(a.trait_ref, b.trait_ref)?,
-            polarity: relation.relate(a.polarity, b.polarity)?,
-        })
-    }
-}
-
-impl<'tcx> Relate<'tcx> for Term<'tcx> {
-    fn relate<R: TypeRelation<'tcx>>(
+impl<'tcx> Relate<TyCtxt<'tcx>> for ty::Term<'tcx> {
+    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
         a: Self,
         b: Self,
     ) -> RelateResult<'tcx, Self> {
         Ok(match (a.unpack(), b.unpack()) {
-            (TermKind::Ty(a), TermKind::Ty(b)) => relation.relate(a, b)?.into(),
-            (TermKind::Const(a), TermKind::Const(b)) => relation.relate(a, b)?.into(),
+            (ty::TermKind::Ty(a), ty::TermKind::Ty(b)) => relation.relate(a, b)?.into(),
+            (ty::TermKind::Const(a), ty::TermKind::Const(b)) => relation.relate(a, b)?.into(),
             _ => return Err(TypeError::Mismatch),
         })
     }
 }
-
-///////////////////////////////////////////////////////////////////////////
-// Error handling
-
-pub fn expected_found<T>(a: T, b: T) -> ExpectedFound<T> {
-    ExpectedFound::new(true, a, b)
-}
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index cf4854d1364..cc6b1d57f87 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -296,7 +296,6 @@ TrivialTypeTraversalImpls! {
     ::rustc_target::abi::FieldIdx,
     ::rustc_target::abi::VariantIdx,
     crate::middle::region::Scope,
-    crate::ty::FloatTy,
     ::rustc_ast::InlineAsmOptions,
     ::rustc_ast::InlineAsmTemplatePiece,
     ::rustc_ast::NodeId,
@@ -316,7 +315,7 @@ TrivialTypeTraversalImpls! {
     crate::traits::Reveal,
     crate::ty::adjustment::AutoBorrowMutability,
     crate::ty::AdtKind,
-    crate::ty::BoundConstness,
+    crate::ty::BoundRegion,
     // Including `BoundRegionKind` is a *bit* dubious, but direct
     // references to bound region appear in `ty::Error`, and aren't
     // really meant to be folded. In general, we can only fold a fully
@@ -324,16 +323,11 @@ TrivialTypeTraversalImpls! {
     crate::ty::BoundRegionKind,
     crate::ty::AssocItem,
     crate::ty::AssocKind,
-    crate::ty::AliasTyKind,
     crate::ty::Placeholder<crate::ty::BoundRegion>,
     crate::ty::Placeholder<crate::ty::BoundTy>,
     crate::ty::Placeholder<ty::BoundVar>,
     crate::ty::LateParamRegion,
-    crate::ty::InferTy,
-    crate::ty::IntVarValue,
     crate::ty::adjustment::PointerCoercion,
-    crate::ty::RegionVid,
-    crate::ty::Variance,
     ::rustc_span::Span,
     ::rustc_span::symbol::Ident,
     ::rustc_errors::ErrorGuaranteed,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index c83f6b0b9ec..ba9ed0d5b70 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -810,6 +810,31 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         Ty::new_alias(interner, kind, alias_ty)
     }
 
+    fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
+        Ty::new_error(interner, guar)
+    }
+
+    fn new_adt(
+        interner: TyCtxt<'tcx>,
+        adt_def: ty::AdtDef<'tcx>,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Self {
+        Ty::new_adt(interner, adt_def, args)
+    }
+
+    fn new_foreign(interner: TyCtxt<'tcx>, def_id: DefId) -> Self {
+        Ty::new_foreign(interner, def_id)
+    }
+
+    fn new_dynamic(
+        interner: TyCtxt<'tcx>,
+        preds: &'tcx List<ty::PolyExistentialPredicate<'tcx>>,
+        region: ty::Region<'tcx>,
+        kind: ty::DynKind,
+    ) -> Self {
+        Ty::new_dynamic(interner, preds, region, kind)
+    }
+
     fn new_coroutine(
         interner: TyCtxt<'tcx>,
         def_id: DefId,
@@ -818,6 +843,51 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         Ty::new_coroutine(interner, def_id, args)
     }
 
+    fn new_coroutine_closure(
+        interner: TyCtxt<'tcx>,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Self {
+        Ty::new_coroutine_closure(interner, def_id, args)
+    }
+
+    fn new_closure(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self {
+        Ty::new_closure(interner, def_id, args)
+    }
+
+    fn new_coroutine_witness(
+        interner: TyCtxt<'tcx>,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> Self {
+        Ty::new_coroutine_witness(interner, def_id, args)
+    }
+
+    fn new_ptr(interner: TyCtxt<'tcx>, ty: Self, mutbl: hir::Mutability) -> Self {
+        Ty::new_ptr(interner, ty, mutbl)
+    }
+
+    fn new_ref(
+        interner: TyCtxt<'tcx>,
+        region: ty::Region<'tcx>,
+        ty: Self,
+        mutbl: hir::Mutability,
+    ) -> Self {
+        Ty::new_ref(interner, region, ty, mutbl)
+    }
+
+    fn new_array_with_const_len(interner: TyCtxt<'tcx>, ty: Self, len: ty::Const<'tcx>) -> Self {
+        Ty::new_array_with_const_len(interner, ty, len)
+    }
+
+    fn new_slice(interner: TyCtxt<'tcx>, ty: Self) -> Self {
+        Ty::new_slice(interner, ty)
+    }
+
+    fn new_tup(interner: TyCtxt<'tcx>, tys: &[Ty<'tcx>]) -> Self {
+        Ty::new_tup(interner, tys)
+    }
+
     fn new_tup_from_iter<It, T>(interner: TyCtxt<'tcx>, iter: It) -> T::Output
     where
         It: Iterator<Item = T>,
@@ -844,6 +914,18 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
     ) -> Self {
         Ty::from_coroutine_closure_kind(interner, kind)
     }
+
+    fn new_fn_def(interner: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Self {
+        Ty::new_fn_def(interner, def_id, args)
+    }
+
+    fn new_fn_ptr(interner: TyCtxt<'tcx>, sig: ty::Binder<'tcx, ty::FnSig<'tcx>>) -> Self {
+        Ty::new_fn_ptr(interner, sig)
+    }
+
+    fn new_pat(interner: TyCtxt<'tcx>, ty: Self, pat: ty::Pattern<'tcx>) -> Self {
+        Ty::new_pat(interner, ty, pat)
+    }
 }
 
 /// Type utilities
@@ -1812,43 +1894,6 @@ impl<'tcx> rustc_type_ir::inherent::Tys<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx
     }
 }
 
-/// Extra information about why we ended up with a particular variance.
-/// This is only used to add more information to error messages, and
-/// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo`
-/// may lead to confusing notes in error messages, it will never cause
-/// a miscompilation or unsoundness.
-///
-/// When in doubt, use `VarianceDiagInfo::default()`
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
-pub enum VarianceDiagInfo<'tcx> {
-    /// No additional information - this is the default.
-    /// We will not add any additional information to error messages.
-    #[default]
-    None,
-    /// We switched our variance because a generic argument occurs inside
-    /// the invariant generic argument of another type.
-    Invariant {
-        /// The generic type containing the generic parameter
-        /// that changes the variance (e.g. `*mut T`, `MyStruct<T>`)
-        ty: Ty<'tcx>,
-        /// The index of the generic parameter being used
-        /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`)
-        param_index: u32,
-    },
-}
-
-impl<'tcx> VarianceDiagInfo<'tcx> {
-    /// Mirrors `Variance::xform` - used to 'combine' the existing
-    /// and new `VarianceDiagInfo`s when our variance changes.
-    pub fn xform(self, other: VarianceDiagInfo<'tcx>) -> VarianceDiagInfo<'tcx> {
-        // For now, just use the first `VarianceDiagInfo::Invariant` that we see
-        match self {
-            VarianceDiagInfo::None => other,
-            VarianceDiagInfo::Invariant { .. } => self,
-        }
-    }
-}
-
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 69ea9c9843a..24e3e623ff2 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -217,10 +217,6 @@ pub struct TypeckResults<'tcx> {
 
     /// Container types and field indices of `offset_of!` expressions
     offset_of_data: ItemLocalMap<(Ty<'tcx>, Vec<(VariantIdx, FieldIdx)>)>,
-
-    /// Maps from `HirId`s of const blocks (the `ExprKind::ConstBlock`, not the inner expression's)
-    /// to the `DefId` of the corresponding inline const.
-    pub inline_consts: FxIndexMap<ItemLocalId, LocalDefId>,
 }
 
 impl<'tcx> TypeckResults<'tcx> {
@@ -253,7 +249,6 @@ impl<'tcx> TypeckResults<'tcx> {
             treat_byte_string_as_slice: Default::default(),
             closure_size_eval: Default::default(),
             offset_of_data: Default::default(),
-            inline_consts: Default::default(),
         }
     }
 
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 14d1b502474..193f0d124bb 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -568,8 +568,11 @@ fn construct_const<'a, 'tcx>(
             ..
         }) => (*span, ty.span),
         Node::AnonConst(ct) => (ct.span, ct.span),
-        Node::Expr(&hir::Expr { span, kind: hir::ExprKind::ConstBlock(_), .. }) => (span, span),
-        node => span_bug!(tcx.def_span(def), "can't build MIR for {def:?}: {node:#?}"),
+        Node::ConstBlock(_) => {
+            let span = tcx.def_span(def);
+            (span, span)
+        }
+        _ => span_bug!(tcx.def_span(def), "can't build MIR for {:?}", def),
     };
 
     let infcx = tcx.infer_ctxt().build();
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index bd66257e6b6..28f9300b97a 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -671,9 +671,9 @@ impl<'tcx> Cx<'tcx> {
                 ExprKind::OffsetOf { container, fields }
             }
 
-            hir::ExprKind::ConstBlock(body) => {
-                let ty = self.typeck_results().node_type(body.hir_id);
-                let did = self.typeck_results().inline_consts[&expr.hir_id.local_id].into();
+            hir::ExprKind::ConstBlock(ref anon_const) => {
+                let ty = self.typeck_results().node_type(anon_const.hir_id);
+                let did = anon_const.def_id.to_def_id();
                 let typeck_root_def_id = tcx.typeck_root_def_id(did);
                 let parent_args =
                     tcx.erase_regions(GenericArgs::identity_for_item(tcx, typeck_root_def_id));
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index bd9e34ae80f..244ac409fd3 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -165,7 +165,7 @@ impl<'tcx> Cx<'tcx> {
         &'a mut self,
         owner_id: HirId,
         fn_decl: &'tcx hir::FnDecl<'tcx>,
-        body: &hir::Body<'tcx>,
+        body: &'tcx hir::Body<'tcx>,
     ) -> impl Iterator<Item = Param<'tcx>> + 'a {
         let fn_sig = self.typeck_results.liberated_fn_sigs()[owner_id];
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 145a40ca3cd..158ca91fcf1 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -637,13 +637,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     /// Converts inline const patterns.
     fn lower_inline_const(
         &mut self,
-        expr: &'tcx hir::Expr<'tcx>,
+        block: &'tcx hir::ConstBlock,
         id: hir::HirId,
         span: Span,
     ) -> PatKind<'tcx> {
         let tcx = self.tcx;
-        let def_id = self.typeck_results.inline_consts[&id.local_id];
-        let ty = tcx.typeck(def_id).node_type(expr.hir_id);
+        let def_id = block.def_id;
+        let body_id = block.body;
+        let expr = &tcx.hir().body(body_id).value;
+        let ty = tcx.typeck(def_id).node_type(block.hir_id);
 
         // Special case inline consts that are just literals. This is solely
         // a performance optimization, as we could also just go through the regular
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index a8741254ffb..e4670633914 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -211,7 +211,7 @@ fn remap_mir_for_const_eval_select<'tcx>(
 }
 
 fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
-    tcx.hir().maybe_body_owned_by(def_id).is_some()
+    tcx.mir_keys(()).contains(&def_id)
 }
 
 /// Finds the full set of `DefId`s within the current crate that have
@@ -222,16 +222,6 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> {
     // All body-owners have MIR associated with them.
     set.extend(tcx.hir().body_owners());
 
-    // Inline consts' bodies are created in
-    // typeck instead of during ast lowering, like all other bodies so far.
-    for def_id in tcx.hir().body_owners() {
-        // Incremental performance optimization: only load typeck results for things that actually have inline consts
-        if tcx.hir_owner_nodes(tcx.hir().body_owned_by(def_id).id().hir_id.owner).has_inline_consts
-        {
-            set.extend(tcx.typeck(def_id).inline_consts.values())
-        }
-    }
-
     // Additionally, tuple struct/variant constructors have MIR, but
     // they don't have a BodyId, so we need to build them separately.
     struct GatherCtors<'a> {
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 9487692662b..61680dbfaf5 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -207,8 +207,8 @@
 
 mod move_check;
 
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{par_for_each_in, LRef, MTLock};
+use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
@@ -251,10 +251,10 @@ pub enum MonoItemCollectionStrategy {
 
 pub struct UsageMap<'tcx> {
     // Maps every mono item to the mono items used by it.
-    used_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
+    used_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
 
     // Maps every mono item to the mono items that use it.
-    user_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
+    user_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
 }
 
 type MonoItems<'tcx> = Vec<Spanned<MonoItem<'tcx>>>;
@@ -262,10 +262,10 @@ type MonoItems<'tcx> = Vec<Spanned<MonoItem<'tcx>>>;
 /// The state that is shared across the concurrent threads that are doing collection.
 struct SharedState<'tcx> {
     /// Items that have been or are currently being recursively collected.
-    visited: MTLock<FxHashSet<MonoItem<'tcx>>>,
+    visited: MTLock<UnordSet<MonoItem<'tcx>>>,
     /// Items that have been or are currently being recursively treated as "mentioned", i.e., their
     /// consts are evaluated but nothing is added to the collection.
-    mentioned: MTLock<FxHashSet<MonoItem<'tcx>>>,
+    mentioned: MTLock<UnordSet<MonoItem<'tcx>>>,
     /// Which items are being used where, for better errors.
     usage_map: MTLock<UsageMap<'tcx>>,
 }
@@ -290,7 +290,7 @@ enum CollectionMode {
 
 impl<'tcx> UsageMap<'tcx> {
     fn new() -> UsageMap<'tcx> {
-        UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() }
+        UsageMap { used_map: Default::default(), user_map: Default::default() }
     }
 
     fn record_used<'a>(
@@ -668,7 +668,7 @@ struct MirUsedCollector<'a, 'tcx> {
     used_items: &'a mut MonoItems<'tcx>,
     /// See the comment in `collect_items_of_instance` for the purpose of this set.
     /// Note that this contains *not-monomorphized* items!
-    used_mentioned_items: &'a mut FxHashSet<MentionedItem<'tcx>>,
+    used_mentioned_items: &'a mut UnordSet<MentionedItem<'tcx>>,
     instance: Instance<'tcx>,
     visiting_call_terminator: bool,
     move_check: move_check::MoveCheckState,
@@ -1272,7 +1272,7 @@ fn collect_items_of_instance<'tcx>(
     // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already
     // added to `used_items` in a hash set, which can efficiently query in the
     // `body.mentioned_items` loop below without even having to monomorphize the item.
-    let mut used_mentioned_items = FxHashSet::<MentionedItem<'tcx>>::default();
+    let mut used_mentioned_items = Default::default();
     let mut collector = MirUsedCollector {
         tcx,
         body,
@@ -1628,10 +1628,10 @@ fn create_mono_items_for_default_impls<'tcx>(
 //=-----------------------------------------------------------------------------
 
 #[instrument(skip(tcx, strategy), level = "debug")]
-pub fn collect_crate_mono_items(
-    tcx: TyCtxt<'_>,
+pub(crate) fn collect_crate_mono_items<'tcx>(
+    tcx: TyCtxt<'tcx>,
     strategy: MonoItemCollectionStrategy,
-) -> (FxHashSet<MonoItem<'_>>, UsageMap<'_>) {
+) -> (Vec<MonoItem<'tcx>>, UsageMap<'tcx>) {
     let _prof_timer = tcx.prof.generic_activity("monomorphization_collector");
 
     let roots = tcx
@@ -1641,8 +1641,8 @@ pub fn collect_crate_mono_items(
     debug!("building mono item graph, beginning at roots");
 
     let mut state = SharedState {
-        visited: MTLock::new(FxHashSet::default()),
-        mentioned: MTLock::new(FxHashSet::default()),
+        visited: MTLock::new(UnordSet::default()),
+        mentioned: MTLock::new(UnordSet::default()),
         usage_map: MTLock::new(UsageMap::new()),
     };
     let recursion_limit = tcx.recursion_limit();
@@ -1665,5 +1665,11 @@ pub fn collect_crate_mono_items(
         });
     }
 
-    (state.visited.into_inner(), state.usage_map.into_inner())
+    // The set of MonoItems was created in an inherently indeterministic order because
+    // of parallelism. We sort it here to ensure that the output is deterministic.
+    let mono_items = tcx.with_stable_hashing_context(move |ref hcx| {
+        state.visited.into_inner().into_sorted(hcx, true)
+    });
+
+    (mono_items, state.usage_map.into_inner())
 }
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index b298fe5813f..eb5f8d92603 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -1,6 +1,5 @@
 #![feature(array_windows)]
 #![feature(is_sorted)]
-#![allow(rustc::potential_query_instability)]
 
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::bug;
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 21d52004728..336341f4e74 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -98,8 +98,9 @@ use std::fs::{self, File};
 use std::io::{BufWriter, Write};
 use std::path::{Path, PathBuf};
 
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sync;
+use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
 use rustc_hir::definitions::DefPathDataName;
@@ -131,7 +132,7 @@ struct PlacedMonoItems<'tcx> {
     /// The codegen units, sorted by name to make things deterministic.
     codegen_units: Vec<CodegenUnit<'tcx>>,
 
-    internalization_candidates: FxHashSet<MonoItem<'tcx>>,
+    internalization_candidates: UnordSet<MonoItem<'tcx>>,
 }
 
 // The output CGUs are sorted by name.
@@ -197,9 +198,9 @@ fn place_mono_items<'tcx, I>(cx: &PartitioningCx<'_, 'tcx>, mono_items: I) -> Pl
 where
     I: Iterator<Item = MonoItem<'tcx>>,
 {
-    let mut codegen_units = FxHashMap::default();
+    let mut codegen_units = UnordMap::default();
     let is_incremental_build = cx.tcx.sess.opts.incremental.is_some();
-    let mut internalization_candidates = FxHashSet::default();
+    let mut internalization_candidates = UnordSet::default();
 
     // Determine if monomorphizations instantiated in this crate will be made
     // available to downstream crates. This depends on whether we are in
@@ -209,7 +210,7 @@ where
         cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics();
 
     let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
-    let cgu_name_cache = &mut FxHashMap::default();
+    let cgu_name_cache = &mut UnordMap::default();
 
     for mono_item in mono_items {
         // Handle only root (GloballyShared) items directly here. Inlined (LocalCopy) items
@@ -260,7 +261,7 @@ where
         // going via another root item. This includes drop-glue, functions from
         // external crates, and local functions the definition of which is
         // marked with `#[inline]`.
-        let mut reachable_inlined_items = FxHashSet::default();
+        let mut reachable_inlined_items = FxIndexSet::default();
         get_reachable_inlined_items(cx.tcx, mono_item, cx.usage_map, &mut reachable_inlined_items);
 
         // Add those inlined items. It's possible an inlined item is reachable
@@ -284,8 +285,9 @@ where
         codegen_units.insert(cgu_name, CodegenUnit::new(cgu_name));
     }
 
-    let mut codegen_units: Vec<_> = codegen_units.into_values().collect();
-    codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
+    let mut codegen_units: Vec<_> = cx.tcx.with_stable_hashing_context(|ref hcx| {
+        codegen_units.into_items().map(|(_, cgu)| cgu).collect_sorted(hcx, true)
+    });
 
     for cgu in codegen_units.iter_mut() {
         cgu.compute_size_estimate();
@@ -297,7 +299,7 @@ where
         tcx: TyCtxt<'tcx>,
         item: MonoItem<'tcx>,
         usage_map: &UsageMap<'tcx>,
-        visited: &mut FxHashSet<MonoItem<'tcx>>,
+        visited: &mut FxIndexSet<MonoItem<'tcx>>,
     ) {
         usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| {
             let is_new = visited.insert(inlined_item);
@@ -320,7 +322,7 @@ fn merge_codegen_units<'tcx>(
     assert!(codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str()));
 
     // This map keeps track of what got merged into what.
-    let mut cgu_contents: FxHashMap<Symbol, Vec<Symbol>> =
+    let mut cgu_contents: UnordMap<Symbol, Vec<Symbol>> =
         codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect();
 
     // If N is the maximum number of CGUs, and the CGUs are sorted from largest
@@ -422,22 +424,24 @@ fn merge_codegen_units<'tcx>(
         // For CGUs that contain the code of multiple modules because of the
         // merging done above, we use a concatenation of the names of all
         // contained CGUs.
-        let new_cgu_names: FxHashMap<Symbol, String> = cgu_contents
-            .into_iter()
-            // This `filter` makes sure we only update the name of CGUs that
-            // were actually modified by merging.
-            .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
-            .map(|(current_cgu_name, cgu_contents)| {
-                let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| s.as_str()).collect();
-
-                // Sort the names, so things are deterministic and easy to
-                // predict. We are sorting primitive `&str`s here so we can
-                // use unstable sort.
-                cgu_contents.sort_unstable();
-
-                (current_cgu_name, cgu_contents.join("--"))
-            })
-            .collect();
+        let new_cgu_names = UnordMap::from(
+            cgu_contents
+                .items()
+                // This `filter` makes sure we only update the name of CGUs that
+                // were actually modified by merging.
+                .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
+                .map(|(current_cgu_name, cgu_contents)| {
+                    let mut cgu_contents: Vec<&str> =
+                        cgu_contents.iter().map(|s| s.as_str()).collect();
+
+                    // Sort the names, so things are deterministic and easy to
+                    // predict. We are sorting primitive `&str`s here so we can
+                    // use unstable sort.
+                    cgu_contents.sort_unstable();
+
+                    (*current_cgu_name, cgu_contents.join("--"))
+                }),
+        );
 
         for cgu in codegen_units.iter_mut() {
             if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
@@ -511,7 +515,7 @@ fn compute_inlined_overlap<'tcx>(cgu1: &CodegenUnit<'tcx>, cgu2: &CodegenUnit<'t
 fn internalize_symbols<'tcx>(
     cx: &PartitioningCx<'_, 'tcx>,
     codegen_units: &mut [CodegenUnit<'tcx>],
-    internalization_candidates: FxHashSet<MonoItem<'tcx>>,
+    internalization_candidates: UnordSet<MonoItem<'tcx>>,
 ) {
     /// For symbol internalization, we need to know whether a symbol/mono-item
     /// is used from outside the codegen unit it is defined in. This type is
@@ -522,7 +526,7 @@ fn internalize_symbols<'tcx>(
         MultipleCgus,
     }
 
-    let mut mono_item_placements = FxHashMap::default();
+    let mut mono_item_placements = UnordMap::default();
     let single_codegen_unit = codegen_units.len() == 1;
 
     if !single_codegen_unit {
@@ -739,7 +743,7 @@ fn mono_item_linkage_and_visibility<'tcx>(
     (Linkage::External, vis)
 }
 
-type CguNameCache = FxHashMap<(DefId, bool), Symbol>;
+type CguNameCache = UnordMap<(DefId, bool), Symbol>;
 
 fn static_visibility<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -932,7 +936,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<
         //
         // Also, unreached inlined items won't be counted here. This is fine.
 
-        let mut inlined_items = FxHashSet::default();
+        let mut inlined_items = UnordSet::default();
 
         let mut root_items = 0;
         let mut unique_inlined_items = 0;
@@ -1164,7 +1168,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co
     }
 
     if tcx.sess.opts.unstable_opts.print_mono_items.is_some() {
-        let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
+        let mut item_to_cgus: UnordMap<_, Vec<_>> = Default::default();
 
         for cgu in codegen_units {
             for (&mono_item, &data) in cgu.items() {
@@ -1240,7 +1244,7 @@ fn dump_mono_items_stats<'tcx>(
     let mut file = BufWriter::new(file);
 
     // Gather instantiated mono items grouped by def_id
-    let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default();
+    let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default();
     for cgu in codegen_units {
         cgu.items()
             .keys()
diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml
index 8bcc21d82f8..c30d21fd784 100644
--- a/compiler/rustc_next_trait_solver/Cargo.toml
+++ b/compiler/rustc_next_trait_solver/Cargo.toml
@@ -4,13 +4,16 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
+# tidy-alphabetical-start
 derivative = "2.2.0"
+rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
+rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_macros = { path = "../rustc_macros", optional = true }
-rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
-rustc_data_structures = { path = "../rustc_data_structures", optional = true }
-rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
+rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
+rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
+tracing = "0.1"
+# tidy-alphabetical-end
 
 [features]
 default = ["nightly"]
diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs
index 92e05cc4901..5c00b6978d6 100644
--- a/compiler/rustc_next_trait_solver/src/resolve.rs
+++ b/compiler/rustc_next_trait_solver/src/resolve.rs
@@ -7,11 +7,11 @@ use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 // EAGER RESOLUTION
 
 /// Resolves ty, region, and const vars to their inferred values or their root vars.
-pub struct EagerResolver<
-    'a,
+pub struct EagerResolver<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
+where
     Infcx: InferCtxtLike<Interner = I>,
-    I: Interner = <Infcx as InferCtxtLike>::Interner,
-> {
+    I: Interner,
+{
     infcx: &'a Infcx,
 }
 
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 04f855e4f55..f678d11213c 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -622,8 +622,6 @@ parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allo
 parse_out_of_range_hex_escape = out of range hex escape
     .label = must be a character in the range [\x00-\x7f]
 
-parse_outer_attr_ambiguous = ambiguous outer attributes
-
 parse_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them
 
 parse_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 3f08a830b0c..6c1fcbe06fc 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -496,15 +496,6 @@ pub(crate) struct OuterAttributeNotAllowedOnIfElse {
 }
 
 #[derive(Diagnostic)]
-#[diag(parse_outer_attr_ambiguous)]
-pub(crate) struct AmbiguousOuterAttributes {
-    #[primary_span]
-    pub span: Span,
-    #[subdiagnostic]
-    pub sugg: WrapInParentheses,
-}
-
-#[derive(Diagnostic)]
 #[diag(parse_missing_in_in_for_loop)]
 pub(crate) struct MissingInInForLoop {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 4acc610d8c4..58fef9b6c45 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -7,7 +7,7 @@ use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Delimiter};
 use rustc_errors::{codes::*, Diag, PResult};
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{sym, symbol::kw, BytePos, Span};
 use thin_vec::ThinVec;
 use tracing::debug;
 
@@ -252,9 +252,23 @@ impl<'a> Parser<'a> {
         maybe_whole!(self, NtMeta, |attr| attr.into_inner());
 
         let do_parse = |this: &mut Self| {
+            let is_unsafe = this.eat_keyword(kw::Unsafe);
+            let unsafety = if is_unsafe {
+                let unsafe_span = this.prev_token.span;
+                this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
+                this.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
+
+                ast::Safety::Unsafe(unsafe_span)
+            } else {
+                ast::Safety::Default
+            };
+
             let path = this.parse_path(PathStyle::Mod)?;
             let args = this.parse_attr_args()?;
-            Ok(ast::AttrItem { path, args, tokens: None })
+            if is_unsafe {
+                this.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
+            }
+            Ok(ast::AttrItem { unsafety, path, args, tokens: None })
         };
         // Attr items don't have attributes
         if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }
@@ -375,10 +389,25 @@ impl<'a> Parser<'a> {
         }
 
         let lo = self.token.span;
+        let is_unsafe = self.eat_keyword(kw::Unsafe);
+        let unsafety = if is_unsafe {
+            let unsafe_span = self.prev_token.span;
+            self.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
+            self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
+
+            ast::Safety::Unsafe(unsafe_span)
+        } else {
+            ast::Safety::Default
+        };
+
         let path = self.parse_path(PathStyle::Mod)?;
         let kind = self.parse_meta_item_kind()?;
+        if is_unsafe {
+            self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
+        }
         let span = lo.to(self.prev_token.span);
-        Ok(ast::MetaItem { path, kind, span })
+
+        Ok(ast::MetaItem { unsafety, path, kind, span })
     }
 
     pub(crate) fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 9677eea0604..2bb6fb53869 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -130,14 +130,14 @@ pub enum AttemptLocalParseRecovery {
 }
 
 impl AttemptLocalParseRecovery {
-    pub fn yes(&self) -> bool {
+    pub(super) fn yes(&self) -> bool {
         match self {
             AttemptLocalParseRecovery::Yes => true,
             AttemptLocalParseRecovery::No => false,
         }
     }
 
-    pub fn no(&self) -> bool {
+    pub(super) fn no(&self) -> bool {
         match self {
             AttemptLocalParseRecovery::Yes => false,
             AttemptLocalParseRecovery::No => true,
@@ -891,7 +891,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn maybe_suggest_struct_literal(
+    pub(super) fn maybe_suggest_struct_literal(
         &mut self,
         lo: Span,
         s: BlockCheckMode,
@@ -2459,7 +2459,7 @@ impl<'a> Parser<'a> {
     /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this
     /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks
     /// like the user has forgotten them.
-    pub fn handle_ambiguous_unbraced_const_arg(
+    pub(super) fn handle_ambiguous_unbraced_const_arg(
         &mut self,
         args: &mut ThinVec<AngleBracketedArg>,
     ) -> PResult<'a, bool> {
@@ -2500,7 +2500,7 @@ impl<'a> Parser<'a> {
     /// - Single-segment paths (i.e. standalone generic const parameters).
     /// All other expressions that can be parsed will emit an error suggesting the expression be
     /// wrapped in braces.
-    pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
+    pub(super) fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P<Expr>> {
         let start = self.token.span;
         let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| {
             err.span_label(
@@ -2559,7 +2559,7 @@ impl<'a> Parser<'a> {
         Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
     }
 
-    pub fn recover_const_param_declaration(
+    pub(super) fn recover_const_param_declaration(
         &mut self,
         ty_generics: Option<&Generics>,
     ) -> PResult<'a, Option<GenericArg>> {
@@ -2589,7 +2589,11 @@ impl<'a> Parser<'a> {
     /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest
     /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion
     /// if we think that the resulting expression would be well formed.
-    pub fn recover_const_arg(&mut self, start: Span, mut err: Diag<'a>) -> PResult<'a, GenericArg> {
+    pub(super) fn recover_const_arg(
+        &mut self,
+        start: Span,
+        mut err: Diag<'a>,
+    ) -> PResult<'a, GenericArg> {
         let is_op_or_dot = AssocOp::from_token(&self.token)
             .and_then(|op| {
                 if let AssocOp::Greater
@@ -2690,7 +2694,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Creates a dummy const argument, and reports that the expression must be enclosed in braces
-    pub fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg {
+    pub(super) fn dummy_const_arg_needs_braces(&self, mut err: Diag<'a>, span: Span) -> GenericArg {
         err.multipart_suggestion(
             "expressions must be enclosed in braces to be used as const generic \
              arguments",
@@ -2961,7 +2965,7 @@ impl<'a> Parser<'a> {
     /// * `=====`
     /// * `<<<<<`
     ///
-    pub fn is_vcs_conflict_marker(
+    pub(super) fn is_vcs_conflict_marker(
         &mut self,
         long_kind: &TokenKind,
         short_kind: &TokenKind,
@@ -2981,14 +2985,14 @@ impl<'a> Parser<'a> {
         None
     }
 
-    pub fn recover_vcs_conflict_marker(&mut self) {
+    pub(super) fn recover_vcs_conflict_marker(&mut self) {
         if let Err(err) = self.err_vcs_conflict_marker() {
             err.emit();
             FatalError.raise();
         }
     }
 
-    pub fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> {
+    pub(crate) fn err_vcs_conflict_marker(&mut self) -> PResult<'a, ()> {
         let Some(start) = self.conflict_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt)
         else {
             return Ok(());
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 1b99bc015b6..e15d6ab2123 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -328,9 +328,7 @@ impl<'a> Parser<'a> {
                 this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::NotYetParsed)
             })?;
 
-            self.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span);
-            let span = lhs_span.to(rhs.span);
-
+            let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
             lhs = match op {
                 AssocOp::Add
                 | AssocOp::Subtract
@@ -429,23 +427,11 @@ impl<'a> Parser<'a> {
         });
     }
 
-    fn error_ambiguous_outer_attrs(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) {
-        if let Some(attr) = lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer) {
-            self.dcx().emit_err(errors::AmbiguousOuterAttributes {
-                span: attr.span.to(rhs_span),
-                sugg: errors::WrapInParentheses::Expression {
-                    left: attr.span.shrink_to_lo(),
-                    right: lhs_span.shrink_to_hi(),
-                },
-            });
-        }
-    }
-
     /// Possibly translate the current token to an associative operator.
     /// The method does not advance the current token.
     ///
     /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively.
-    pub fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
+    pub(super) fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
         let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) {
             // When parsing const expressions, stop parsing when encountering `>`.
             (
@@ -520,8 +506,7 @@ impl<'a> Parser<'a> {
             None
         };
         let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span);
-        self.error_ambiguous_outer_attrs(&lhs, lhs.span, rhs_span);
-        let span = lhs.span.to(rhs_span);
+        let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span);
         let limits =
             if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed };
         let range = self.mk_range(Some(lhs), rhs, limits);
@@ -739,8 +724,7 @@ impl<'a> Parser<'a> {
         expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind,
     ) -> PResult<'a, P<Expr>> {
         let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| {
-            this.error_ambiguous_outer_attrs(&lhs, lhs_span, rhs.span);
-            this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs))
+            this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs))
         };
 
         // Save the state of the parser before parsing type normally, in case there is a
@@ -1022,7 +1006,11 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
+    pub(super) fn parse_dot_suffix_expr(
+        &mut self,
+        lo: Span,
+        base: P<Expr>,
+    ) -> PResult<'a, P<Expr>> {
         // At this point we've consumed something like `expr.` and `self.token` holds the token
         // after the dot.
         match self.token.uninterpolate().kind {
@@ -3858,6 +3846,16 @@ impl<'a> Parser<'a> {
         self.mk_expr(span, ExprKind::Err(guar))
     }
 
+    /// Create expression span ensuring the span of the parent node
+    /// is larger than the span of lhs and rhs, including the attributes.
+    fn mk_expr_sp(&self, lhs: &P<Expr>, lhs_span: Span, rhs_span: Span) -> Span {
+        lhs.attrs
+            .iter()
+            .find(|a| a.style == AttrStyle::Outer)
+            .map_or(lhs_span, |a| a.span)
+            .to(rhs_span)
+    }
+
     fn collect_tokens_for_expr(
         &mut self,
         attrs: AttrWrapper,
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 37c99958fc8..3f5a4afdad8 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2265,7 +2265,7 @@ pub(crate) struct FnParseMode {
     ///     to true.
     ///   * The span is from Edition 2015. In particular, you can get a
     ///     2015 span inside a 2021 crate using macros.
-    pub req_name: ReqName,
+    pub(super) req_name: ReqName,
     /// If this flag is set to `true`, then plain, semicolon-terminated function
     /// prototypes are not allowed here.
     ///
@@ -2284,7 +2284,7 @@ pub(crate) struct FnParseMode {
     /// This field should only be set to false if the item is inside of a trait
     /// definition or extern block. Within an impl block or a module, it should
     /// always be set to true.
-    pub req_body: bool,
+    pub(super) req_body: bool,
 }
 
 /// Parsing of functions and methods.
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 8f733b4fcbb..adf04fcf224 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -11,22 +11,21 @@ mod stmt;
 mod ty;
 
 use crate::lexer::UnmatchedDelim;
-pub use attr_wrapper::AttrWrapper;
+use attr_wrapper::AttrWrapper;
 pub use diagnostics::AttemptLocalParseRecovery;
 pub(crate) use expr::ForbiddenLetReason;
 pub(crate) use item::FnParseMode;
 pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
-pub use path::PathStyle;
+use path::PathStyle;
 
-use core::fmt;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing};
 use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
 use rustc_ast::util::case::Case;
 use rustc_ast::{
-    self as ast, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs, Expr,
-    ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, Visibility,
+    self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, DelimArgs,
+    Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, Visibility,
     VisibilityKind, DUMMY_NODE_ID,
 };
 use rustc_ast_pretty::pprust;
@@ -37,7 +36,7 @@ use rustc_session::parse::ParseSess;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use std::ops::Range;
-use std::{mem, slice};
+use std::{fmt, mem, slice};
 use thin_vec::ThinVec;
 use tracing::debug;
 
@@ -146,7 +145,7 @@ pub struct Parser<'a> {
     /// The current token.
     pub token: Token,
     /// The spacing for the current token.
-    pub token_spacing: Spacing,
+    token_spacing: Spacing,
     /// The previous token.
     pub prev_token: Token,
     pub capture_cfg: bool,
@@ -187,7 +186,7 @@ pub struct Parser<'a> {
     current_closure: Option<ClosureSpans>,
     /// Whether the parser is allowed to do recovery.
     /// This is disabled when parsing macro arguments, see #103534
-    pub recovery: Recovery,
+    recovery: Recovery,
 }
 
 // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
@@ -197,10 +196,10 @@ rustc_data_structures::static_assert_size!(Parser<'_>, 264);
 
 /// Stores span information about a closure.
 #[derive(Clone, Debug)]
-pub struct ClosureSpans {
-    pub whole_closure: Span,
-    pub closing_pipe: Span,
-    pub body: Span,
+struct ClosureSpans {
+    whole_closure: Span,
+    closing_pipe: Span,
+    body: Span,
 }
 
 /// Indicates a range of tokens that should be replaced by
@@ -220,13 +219,13 @@ pub struct ClosureSpans {
 /// the first macro inner attribute to invoke a proc-macro).
 /// When create a `TokenStream`, the inner attributes get inserted
 /// into the proper place in the token stream.
-pub type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>);
+type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>);
 
 /// Controls how we capture tokens. Capturing can be expensive,
 /// so we try to avoid performing capturing in cases where
 /// we will never need an `AttrTokenStream`.
 #[derive(Copy, Clone, Debug)]
-pub enum Capturing {
+enum Capturing {
     /// We aren't performing any capturing - this is the default mode.
     No,
     /// We are capturing tokens
@@ -374,13 +373,13 @@ pub enum FollowedByType {
 }
 
 #[derive(Copy, Clone, Debug)]
-pub enum Trailing {
+enum Trailing {
     No,
     Yes,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum TokenDescription {
+pub(super) enum TokenDescription {
     ReservedIdentifier,
     Keyword,
     ReservedKeyword,
@@ -388,7 +387,7 @@ pub enum TokenDescription {
 }
 
 impl TokenDescription {
-    pub fn from_token(token: &Token) -> Option<Self> {
+    pub(super) fn from_token(token: &Token) -> Option<Self> {
         match token.kind {
             _ if token.is_special_ident() => Some(TokenDescription::ReservedIdentifier),
             _ if token.is_used_keyword() => Some(TokenDescription::Keyword),
@@ -502,7 +501,7 @@ impl<'a> Parser<'a> {
     /// Expect next token to be edible or inedible token. If edible,
     /// then consume it; if inedible, then return without consuming
     /// anything. Signal a fatal error if next token is unexpected.
-    pub fn expect_one_of(
+    fn expect_one_of(
         &mut self,
         edible: &[TokenKind],
         inedible: &[TokenKind],
@@ -572,7 +571,7 @@ impl<'a> Parser<'a> {
     /// the main purpose of this function is to reduce the cluttering of the suggestions list
     /// which using the normal eat method could introduce in some cases.
     #[inline]
-    pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool {
+    fn eat_noexpect(&mut self, tok: &TokenKind) -> bool {
         let is_present = self.check_noexpect(tok);
         if is_present {
             self.bump()
@@ -1262,9 +1261,12 @@ impl<'a> Parser<'a> {
         }
         self.eat_keyword(kw::Const);
         let (attrs, blk) = self.parse_inner_attrs_and_block()?;
-        let expr = self.mk_expr(blk.span, ExprKind::Block(blk, None));
-        let blk_span = expr.span;
-        Ok(self.mk_expr_with_attrs(span.to(blk_span), ExprKind::ConstBlock(expr), attrs))
+        let anon_const = AnonConst {
+            id: DUMMY_NODE_ID,
+            value: self.mk_expr(blk.span, ExprKind::Block(blk, None)),
+        };
+        let blk_span = anon_const.value.span;
+        Ok(self.mk_expr_with_attrs(span.to(blk_span), ExprKind::ConstBlock(anon_const), attrs))
     }
 
     /// Parses mutability (`mut` or nothing).
@@ -1517,7 +1519,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub fn collect_tokens_no_attrs<R: HasAttrs + HasTokens>(
+    fn collect_tokens_no_attrs<R: HasAttrs + HasTokens>(
         &mut self,
         f: impl FnOnce(&mut Self) -> PResult<'a, R>,
     ) -> PResult<'a, R> {
@@ -1538,8 +1540,10 @@ impl<'a> Parser<'a> {
             })
     }
 
-    // debug view of the parser's token stream, up to `{lookahead}` tokens
-    pub fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ {
+    // Debug view of the parser's token stream, up to `{lookahead}` tokens.
+    // Only used when debugging.
+    #[allow(unused)]
+    pub(crate) fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ {
         struct DebugParser<'dbg> {
             parser: &'dbg Parser<'dbg>,
             lookahead: usize,
@@ -1615,7 +1619,7 @@ pub(crate) fn make_unclosed_delims_error(
 /// is then 'parsed' to build up an `AttrTokenStream` with nested
 /// `AttrTokenTree::Delimited` tokens.
 #[derive(Debug, Clone)]
-pub enum FlatToken {
+enum FlatToken {
     /// A token - this holds both delimiter (e.g. '{' and '}')
     /// and non-delimiter tokens
     Token(Token),
diff --git a/compiler/rustc_parse/src/parser/mut_visit/tests.rs b/compiler/rustc_parse/src/parser/mut_visit/tests.rs
index b3cb28af657..677bcdf7fcd 100644
--- a/compiler/rustc_parse/src/parser/mut_visit/tests.rs
+++ b/compiler/rustc_parse/src/parser/mut_visit/tests.rs
@@ -21,14 +21,12 @@ impl MutVisitor for ToZzIdentMutVisitor {
     }
 }
 
-// Maybe add to `expand.rs`.
-macro_rules! assert_pred {
-    ($pred:expr, $predname:expr, $a:expr , $b:expr) => {{
-        let pred_val = $pred;
+macro_rules! assert_matches_codepattern {
+    ($a:expr , $b:expr) => {{
         let a_val = $a;
         let b_val = $b;
-        if !(pred_val(&a_val, &b_val)) {
-            panic!("expected args satisfying {}, got {} and {}", $predname, a_val, b_val);
+        if !matches_codepattern(&a_val, &b_val) {
+            panic!("expected args satisfying `matches_codepattern`, got {} and {}", a_val, b_val);
         }
     }};
 }
@@ -41,9 +39,7 @@ fn ident_transformation() {
         let mut krate =
             string_to_crate("#[a] mod b {fn c (d : e, f : g) {h!(i,j,k);l;m}}".to_string());
         zz_visitor.visit_crate(&mut krate);
-        assert_pred!(
-            matches_codepattern,
-            "matches_codepattern",
+        assert_matches_codepattern!(
             print_crate_items(&krate),
             "#[zz]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}".to_string()
         );
@@ -61,9 +57,7 @@ fn ident_transformation_in_defs() {
                 .to_string(),
         );
         zz_visitor.visit_crate(&mut krate);
-        assert_pred!(
-            matches_codepattern,
-            "matches_codepattern",
+        assert_matches_codepattern!(
             print_crate_items(&krate),
             "macro_rules! zz{(zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+))}".to_string()
         );
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index fcedc1a4af3..9beecd9849f 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -20,7 +20,7 @@ use tracing::debug;
 
 /// Specifies how to parse a path.
 #[derive(Copy, Clone, PartialEq)]
-pub enum PathStyle {
+pub(super) enum PathStyle {
     /// In some contexts, notably in expressions, paths with generic arguments are ambiguous
     /// with something else. For example, in expressions `segment < ....` can be interpreted
     /// as a comparison and `segment ( ....` can be interpreted as a function call.
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index be539d15386..104aae9b257 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -31,7 +31,7 @@ impl<'a> Parser<'a> {
     /// Parses a statement. This stops just before trailing semicolons on everything but items.
     /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
     // Public for rustfmt usage.
-    pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
+    pub(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
         Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| {
             e.emit();
             self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 2033f387887..5bed0317e5e 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -127,7 +127,7 @@ impl<'a> Parser<'a> {
     /// Parse a type suitable for a field definition.
     /// The difference from `parse_ty` is that this version
     /// allows anonymous structs and unions.
-    pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P<Ty>> {
+    pub(super) fn parse_ty_for_field_def(&mut self) -> PResult<'a, P<Ty>> {
         if self.can_begin_anon_struct_or_union() {
             self.parse_anon_struct_or_union()
         } else {
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index b91ef1ae1f3..19d6f512572 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -25,15 +25,21 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
     match attr_info {
         // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
         Some(BuiltinAttribute { name, template, .. }) if *name != sym::rustc_dummy => {
-            check_builtin_attribute(psess, attr, *name, *template)
+            match parse_meta(psess, attr) {
+                Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, *name, *template),
+                Err(err) => {
+                    err.emit();
+                }
+            }
         }
         _ if let AttrArgs::Eq(..) = attr.get_normal_item().args => {
             // All key-value attributes are restricted to meta-item syntax.
-            parse_meta(psess, attr)
-                .map_err(|err| {
+            match parse_meta(psess, attr) {
+                Ok(_) => {}
+                Err(err) => {
                     err.emit();
-                })
-                .ok();
+                }
+            }
         }
         _ => {}
     }
@@ -42,6 +48,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
 pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
     let item = attr.get_normal_item();
     Ok(MetaItem {
+        unsafety: item.unsafety,
         span: attr.span,
         path: item.path.clone(),
         kind: match &item.args {
@@ -103,7 +110,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met
     })
 }
 
-pub fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
+fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
     if let Delimiter::Parenthesis = delim {
         return;
     }
@@ -113,7 +120,7 @@ pub fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter
     });
 }
 
-pub fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
+pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
     if let Delimiter::Parenthesis = delim {
         return;
     }
@@ -133,20 +140,6 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte
     }
 }
 
-pub fn check_builtin_attribute(
-    psess: &ParseSess,
-    attr: &Attribute,
-    name: Symbol,
-    template: AttributeTemplate,
-) {
-    match parse_meta(psess, attr) {
-        Ok(meta) => check_builtin_meta_item(psess, &meta, attr.style, name, template),
-        Err(err) => {
-            err.emit();
-        }
-    }
-}
-
 pub fn check_builtin_meta_item(
     psess: &ParseSess,
     meta: &MetaItem,
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index d850644bb45..07c82065a80 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -384,6 +384,10 @@ passes_invalid_attr_at_crate_level =
 passes_invalid_attr_at_crate_level_item =
     the inner attribute doesn't annotate this {$kind}
 
+passes_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
+    .suggestion = remove the `unsafe(...)`
+    .note = extraneous unsafe is not allowed in attributes
+
 passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument
 
 passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 39cb48c1af3..6ce7c41acc8 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -10,7 +10,9 @@ use rustc_ast::{MetaItemKind, MetaItemLit, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::StashKey;
 use rustc_errors::{Applicability, DiagCtxt, IntoDiagArg, MultiSpan};
-use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
+use rustc_feature::{
+    is_unsafe_attr, AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP,
+};
 use rustc_hir::def_id::LocalModDefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{self as hir};
@@ -114,6 +116,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         let mut seen = FxHashMap::default();
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
+            self.check_unsafe_attr(attr);
+
             match attr.path().as_slice() {
                 [sym::diagnostic, sym::do_not_recommend] => {
                     self.check_do_not_recommend(attr.span, hir_id, target)
@@ -308,6 +312,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         true
     }
 
+    /// Checks if `unsafe()` is applied to an invalid attribute.
+    fn check_unsafe_attr(&self, attr: &Attribute) {
+        if !attr.is_doc_comment() {
+            let attr_item = attr.get_normal_item();
+            if let ast::Safety::Unsafe(unsafe_span) = attr_item.unsafety {
+                if !is_unsafe_attr(attr.name_or_empty()) {
+                    self.dcx().emit_err(errors::InvalidAttrUnsafe {
+                        span: unsafe_span,
+                        name: attr_item.path.clone(),
+                    });
+                }
+            }
+        }
+    }
+
     /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
     fn check_diagnostic_on_unimplemented(
         &self,
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 71f3e8b7b5d..3f6eccbd5a5 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -196,6 +196,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
         self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
     }
 
+    fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) {
+        let kind = Some(hir::ConstContext::Const { inline: true });
+        self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block));
+    }
+
     fn visit_body(&mut self, body: &hir::Body<'tcx>) {
         let owner = self.tcx.hir().body_owner_def_id(body.id());
         let kind = self.tcx.hir().body_const_context(owner);
@@ -223,11 +228,6 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
                     self.const_check_violated(expr, e.span);
                 }
             }
-            hir::ExprKind::ConstBlock(expr) => {
-                let kind = Some(hir::ConstContext::Const { inline: true });
-                self.recurse_into(kind, None, |this| intravisit::walk_expr(this, expr));
-                return;
-            }
 
             _ => {}
         }
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 0049afff528..2cb3c5d8965 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
 use rustc_session::lint::builtin::DEAD_CODE;
@@ -44,16 +44,63 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
     )
 }
 
-fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
+struct Publicness {
+    ty_is_public: bool,
+    ty_and_all_fields_are_public: bool,
+}
+
+impl Publicness {
+    fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self {
+        Self { ty_is_public, ty_and_all_fields_are_public }
+    }
+}
+
+fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
+    // treat PhantomData and positional ZST as public,
+    // we don't want to lint types which only have them,
+    // cause it's a common way to use such types to check things like well-formedness
+    tcx.adt_def(id).all_fields().all(|field| {
+        let field_type = tcx.type_of(field.did).instantiate_identity();
+        if field_type.is_phantom_data() {
+            return true;
+        }
+        let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit());
+        if is_positional
+            && tcx
+                .layout_of(tcx.param_env(field.did).and(field_type))
+                .map_or(true, |layout| layout.is_zst())
+        {
+            return true;
+        }
+        field.vis.is_public()
+    })
+}
+
+/// check struct and its fields are public or not,
+/// for enum and union, just check they are public,
+/// and doesn't solve types like &T for now, just skip them
+fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
     if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
         && let Res::Def(def_kind, def_id) = path.res
         && def_id.is_local()
-        && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
     {
-        tcx.visibility(def_id).is_public()
-    } else {
-        true
+        return match def_kind {
+            DefKind::Enum | DefKind::Union => {
+                let ty_is_public = tcx.visibility(def_id).is_public();
+                Publicness::new(ty_is_public, ty_is_public)
+            }
+            DefKind::Struct => {
+                let ty_is_public = tcx.visibility(def_id).is_public();
+                Publicness::new(
+                    ty_is_public,
+                    ty_is_public && struct_all_fields_are_public(tcx, def_id),
+                )
+            }
+            _ => Publicness::new(true, true),
+        };
     }
+
+    Publicness::new(true, true)
 }
 
 /// Determine if a work from the worklist is coming from the a `#[allow]`
@@ -427,9 +474,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                         {
                             if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
                                 && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
+                                    .ty_and_all_fields_are_public
                             {
-                                // skip methods of private ty,
-                                // they would be solved in `solve_rest_impl_items`
+                                // skip impl-items of non pure pub ty,
+                                // cause we don't know the ty is constructed or not,
+                                // check these later in `solve_rest_impl_items`
                                 continue;
                             }
 
@@ -510,22 +559,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
             && let Some(local_def_id) = def_id.as_local()
             && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
         {
-            if self.tcx.visibility(impl_item_id).is_public() {
-                // for the public method, we don't know the trait item is used or not,
-                // so we mark the method live if the self is used
-                return self.live_symbols.contains(&local_def_id);
-            }
-
             if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
                 && let Some(local_id) = trait_item_id.as_local()
             {
-                // for the private method, we can know the trait item is used or not,
+                // for the local impl item, we can know the trait item is used or not,
                 // so we mark the method live if the self is used and the trait item is used
-                return self.live_symbols.contains(&local_id)
-                    && self.live_symbols.contains(&local_def_id);
+                self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id)
+            } else {
+                // for the foreign method and inherent pub method,
+                // we don't know the trait item or the method is used or not,
+                // so we mark the method live if the self is used
+                self.live_symbols.contains(&local_def_id)
             }
+        } else {
+            false
         }
-        false
     }
 }
 
@@ -587,16 +635,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
             hir::ExprKind::OffsetOf(..) => {
                 self.handle_offset_of(expr);
             }
-            hir::ExprKind::ConstBlock(expr) => {
-                // When inline const blocks are used in pattern position, paths
-                // referenced by it should be considered as used.
-                let in_pat = mem::replace(&mut self.in_pat, false);
-
-                intravisit::walk_expr(self, expr);
-
-                self.in_pat = in_pat;
-                return;
-            }
             _ => (),
         }
 
@@ -658,6 +696,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
 
         self.in_pat = in_pat;
     }
+
+    fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
+        // When inline const blocks are used in pattern position, paths
+        // referenced by it should be considered as used.
+        let in_pat = mem::replace(&mut self.in_pat, false);
+
+        self.live_symbols.insert(c.def_id);
+        intravisit::walk_inline_const(self, c);
+
+        self.in_pat = in_pat;
+    }
 }
 
 fn has_allow_dead_code_or_lang_attr(
@@ -746,7 +795,9 @@ fn check_item<'tcx>(
                 .iter()
                 .filter_map(|def_id| def_id.as_local());
 
-            let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
+            let self_ty = tcx.hir().item(id).expect_impl().self_ty;
+            let Publicness { ty_is_public, ty_and_all_fields_are_public } =
+                ty_ref_to_pub_struct(tcx, self_ty);
 
             // And we access the Map here to get HirId from LocalDefId
             for local_def_id in local_def_ids {
@@ -762,18 +813,20 @@ fn check_item<'tcx>(
                 // for trait impl blocks,
                 // mark the method live if the self_ty is public,
                 // or the method is public and may construct self
-                if of_trait
-                    && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
-                        || tcx.visibility(local_def_id).is_public()
-                            && (ty_is_pub || may_construct_self))
+                if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy)
+                    || tcx.visibility(local_def_id).is_public()
+                        && (ty_and_all_fields_are_public || may_construct_self)
                 {
+                    // if the impl item is public,
+                    // and the ty may be constructed or can be constructed in foreign crates,
+                    // mark the impl item live
                     worklist.push((local_def_id, ComesFromAllowExpect::No));
                 } else if let Some(comes_from_allow) =
                     has_allow_dead_code_or_lang_attr(tcx, local_def_id)
                 {
                     worklist.push((local_def_id, comes_from_allow));
-                } else if of_trait {
-                    // private method || public method not constructs self
+                } else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public {
+                    // private impl items of traits || public impl items not constructs self
                     unsolved_impl_items.push((id, local_def_id));
                 }
             }
@@ -840,6 +893,14 @@ fn create_and_seed_worklist(
             effective_vis
                 .is_public_at_level(Level::Reachable)
                 .then_some(id)
+                .filter(|&id|
+                    // checks impls, impl-items and pub structs with all public fields later
+                    match tcx.def_kind(id) {
+                        DefKind::Impl { .. } => false,
+                        DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
+                        DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()),
+                        _ => true
+                    })
                 .map(|id| (id, ComesFromAllowExpect::No))
         })
         // Seed entry point
@@ -1112,10 +1173,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
             || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
         {
             for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
-                // We have diagnosed unused methods in traits
+                // We have diagnosed unused assoc consts and fns in traits
                 if matches!(def_kind, DefKind::Impl { of_trait: true })
-                    && tcx.def_kind(def_id) == DefKind::AssocFn
-                    || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
+                    && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn)
+                    // skip unused public inherent methods,
+                    // cause we have diagnosed unconstructed struct
+                    || matches!(def_kind, DefKind::Impl { of_trait: false })
+                        && tcx.visibility(def_id).is_public()
+                        && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public
+                    || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy
                 {
                     continue;
                 }
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 7fdd9924b51..a935f1ad7d3 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -4,7 +4,7 @@ use std::{
 };
 
 use crate::fluent_generated as fluent;
-use rustc_ast::Label;
+use rustc_ast::{ast, Label};
 use rustc_errors::{
     codes::*, Applicability, Diag, DiagCtxt, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
     MultiSpan, SubdiagMessageOp, Subdiagnostic,
@@ -863,6 +863,15 @@ pub struct InvalidAttrAtCrateLevel {
     pub item: Option<ItemFollowingInnerAttr>,
 }
 
+#[derive(Diagnostic)]
+#[diag(passes_invalid_attr_unsafe)]
+#[note]
+pub struct InvalidAttrUnsafe {
+    #[primary_span]
+    pub span: Span,
+    pub name: ast::Path,
+}
+
 #[derive(Clone, Copy)]
 pub struct ItemFollowingInnerAttr {
     pub span: Span,
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 698f15015c4..dfa7dfa3398 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -147,11 +147,6 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         return;
     }
 
-    // Don't run for inline consts, they are collected together with their parent
-    if let DefKind::InlineConst = tcx.def_kind(def_id) {
-        return;
-    }
-
     // Don't run unused pass for #[naked]
     if tcx.has_attr(def_id.to_def_id(), sym::naked) {
         return;
@@ -1148,13 +1143,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             }
 
             hir::ExprKind::Lit(..)
+            | hir::ExprKind::ConstBlock(..)
             | hir::ExprKind::Err(_)
             | hir::ExprKind::Path(hir::QPath::TypeRelative(..))
             | hir::ExprKind::Path(hir::QPath::LangItem(..))
             | hir::ExprKind::OffsetOf(..) => succ,
 
-            hir::ExprKind::ConstBlock(expr) => self.propagate_through_expr(expr, succ),
-
             // Note that labels have been resolved, so we don't need to look
             // at the label ident
             hir::ExprKind::Block(ref blk, _) => self.propagate_through_block(blk, succ),
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 737310e5c04..2587a18b8c8 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -93,6 +93,10 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
         self.with_context(Constant, |v| intravisit::walk_anon_const(v, c));
     }
 
+    fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) {
+        self.with_context(Constant, |v| intravisit::walk_inline_const(v, c));
+    }
+
     fn visit_fn(
         &mut self,
         fk: hir::intravisit::FnKind<'hir>,
@@ -285,9 +289,6 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     self.cx_stack.len() - 1,
                 )
             }
-            hir::ExprKind::ConstBlock(expr) => {
-                self.with_context(Constant, |v| intravisit::walk_expr(v, expr));
-            }
             _ => intravisit::walk_expr(self, e),
         }
     }
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index d7416ead325..ca97d10617b 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -340,6 +340,16 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
             ExprKind::Gen(_, _, _) => {
                 self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
             }
+            ExprKind::ConstBlock(ref constant) => {
+                let def = self.create_def(
+                    constant.id,
+                    kw::Empty,
+                    DefKind::InlineConst,
+                    constant.value.span,
+                );
+                self.with_parent(def, |this| visit::walk_anon_const(this, constant));
+                return;
+            }
             _ => self.parent_def,
         };
 
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 78bd3c4e49f..b6a23317dc9 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -998,14 +998,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             let Some(module) = single_import.imported_module.get() else {
                 return Err((Undetermined, Weak::No));
             };
-            let ImportKind::Single { source: ident, source_bindings, .. } = &single_import.kind
+            let ImportKind::Single { source: ident, target, target_bindings, .. } =
+                &single_import.kind
             else {
                 unreachable!();
             };
-            if binding.map_or(false, |binding| binding.module().is_some())
-                && source_bindings.iter().all(|binding| matches!(binding.get(), Err(Undetermined)))
-            {
-                // This branch allows the binding to be defined or updated later,
+            if (ident != target) && target_bindings.iter().all(|binding| binding.get().is_none()) {
+                // This branch allows the binding to be defined or updated later if the target name
+                // can hide the source but these bindings are not obtained.
                 // avoiding module inconsistency between the resolve process and the finalize process.
                 // See more details in #124840
                 return Err((Undetermined, Weak::No));
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index c7c95addf49..b0adc3775f6 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -4535,10 +4535,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                 self.visit_expr(elem);
                 self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::Yes));
             }
-            ExprKind::ConstBlock(ref expr) => {
-                self.resolve_anon_const_manual(false, AnonConstKind::InlineConst, |this| {
-                    this.visit_expr(expr)
-                });
+            ExprKind::ConstBlock(ref ct) => {
+                self.resolve_anon_const(ct, AnonConstKind::InlineConst);
             }
             ExprKind::Index(ref elem, ref idx, _) => {
                 self.resolve_expr(elem, Some(expr));
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 9cb8cd836e6..6f63776bedc 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -12,7 +12,7 @@ use tracing::debug;
 pub struct FileSearch<'a> {
     sysroot: &'a Path,
     triple: &'a str,
-    search_paths: &'a [SearchPath],
+    cli_search_paths: &'a [SearchPath],
     tlib_path: &'a SearchPath,
     kind: PathKind,
 }
@@ -20,7 +20,7 @@ pub struct FileSearch<'a> {
 impl<'a> FileSearch<'a> {
     pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> {
         let kind = self.kind;
-        self.search_paths
+        self.cli_search_paths
             .iter()
             .filter(move |sp| sp.kind.matches(kind))
             .chain(std::iter::once(self.tlib_path))
@@ -37,26 +37,26 @@ impl<'a> FileSearch<'a> {
     pub fn new(
         sysroot: &'a Path,
         triple: &'a str,
-        search_paths: &'a [SearchPath],
+        cli_search_paths: &'a [SearchPath],
         tlib_path: &'a SearchPath,
         kind: PathKind,
     ) -> FileSearch<'a> {
         debug!("using sysroot = {}, triple = {}", sysroot.display(), triple);
-        FileSearch { sysroot, triple, search_paths, tlib_path, kind }
+        FileSearch { sysroot, triple, cli_search_paths, tlib_path, kind }
     }
 }
 
 pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
-    PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")])
+    let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
+    sysroot.join(rustlib_path).join("lib")
 }
 
 /// Returns a path to the target's `bin` folder within its `rustlib` path in the sysroot. This is
 /// where binaries are usually installed, e.g. the self-contained linkers, lld-wrappers, LLVM tools,
 /// etc.
 pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple);
-    PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("bin")])
+    let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
+    sysroot.join(rustlib_path).join("bin")
 }
 
 #[cfg(unix)]
@@ -275,7 +275,7 @@ pub fn get_or_default_sysroot() -> Result<PathBuf, String> {
                 p.pop();
                 p.pop();
                 // Look for the target rustlib directory in the suspected sysroot.
-                let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy");
+                let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy");
                 rustlib_path.pop(); // pop off the dummy target.
                 rustlib_path.exists().then_some(p)
             }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index f530d1dd1d4..93594264167 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1962,6 +1962,7 @@ symbols! {
         unreachable_display,
         unreachable_macro,
         unrestricted_attribute_tokens,
+        unsafe_attributes,
         unsafe_block_in_unsafe_fn,
         unsafe_cell,
         unsafe_cell_raw_get,
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index 46c83be9d95..ecc91ab9a31 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -41,17 +41,13 @@ const RUST_LIB_DIR: &str = "rustlib";
 ///
 /// For example: `target_sysroot_path("/usr", "x86_64-unknown-linux-gnu")` =>
 /// `"lib*/rustlib/x86_64-unknown-linux-gnu"`.
-pub fn target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
-    let libdir = find_libdir(sysroot);
-    PathBuf::from_iter([
-        Path::new(libdir.as_ref()),
-        Path::new(RUST_LIB_DIR),
-        Path::new(target_triple),
-    ])
+pub fn relative_target_rustlib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
+    let libdir = find_relative_libdir(sysroot);
+    Path::new(libdir.as_ref()).join(RUST_LIB_DIR).join(target_triple)
 }
 
 /// The name of the directory rustc expects libraries to be located.
-fn find_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
+fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> {
     // FIXME: This is a quick hack to make the rustc binary able to locate
     // Rust libraries in Linux environments where libraries might be installed
     // to lib64/lib32. This would be more foolproof by basing the sysroot off
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 83ee63e2cf2..fe07d116726 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -3367,7 +3367,7 @@ impl Target {
 
                 // Additionally look in the sysroot under `lib/rustlib/<triple>/target.json`
                 // as a fallback.
-                let rustlib_path = crate::target_rustlib_path(sysroot, target_triple);
+                let rustlib_path = crate::relative_target_rustlib_path(sysroot, target_triple);
                 let p = PathBuf::from_iter([
                     Path::new(sysroot),
                     Path::new(&rustlib_path),
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 521e4ef0c9e..381da6f7e2a 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -23,7 +23,6 @@
 #![feature(extract_if)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
-#![feature(option_take_if)]
 #![feature(never_type)]
 #![feature(type_alias_impl_trait)]
 #![recursion_limit = "512"] // For rustdoc
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index f90e4711037..b522022c206 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -37,11 +37,11 @@ pub(super) mod canonical;
 mod probe;
 mod select;
 
-pub struct EvalCtxt<
-    'a,
+pub struct EvalCtxt<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
+where
     Infcx: InferCtxtLike<Interner = I>,
-    I: Interner = <Infcx as InferCtxtLike>::Interner,
-> {
+    I: Interner,
+{
     /// The inference context that backs (mostly) inference and placeholder terms
     /// instantiated while solving goals.
     ///
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 6a96a03e047..c7da85bd1cc 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -28,7 +28,7 @@ use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
 use rustc_macros::extension;
 use rustc_middle::hir::map;
 use rustc_middle::traits::IsConstable;
-use rustc_middle::ty::error::TypeError::{self, Sorts};
+use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::print::PrintPolyTraitRefExt;
 use rustc_middle::ty::{
     self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs,
@@ -3842,7 +3842,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     && let Some(failed_pred) = failed_pred.as_projection_clause()
                     && let Some(found) = failed_pred.skip_binder().term.as_type()
                 {
-                    type_diffs = vec![Sorts(ty::error::ExpectedFound {
+                    type_diffs = vec![TypeError::Sorts(ty::error::ExpectedFound {
                         expected: where_pred
                             .skip_binder()
                             .projection_term
@@ -3985,7 +3985,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 continue;
             };
             for diff in type_diffs {
-                let Sorts(expected_found) = diff else {
+                let TypeError::Sorts(expected_found) = diff else {
                     continue;
                 };
                 if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
@@ -4165,7 +4165,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     };
                     if primary_spans.is_empty()
                         || type_diffs.iter().any(|diff| {
-                            let Sorts(expected_found) = diff else {
+                            let TypeError::Sorts(expected_found) = diff else {
                                 return false;
                             };
                             self.can_eq(param_env, expected_found.found, ty)
@@ -4198,7 +4198,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc));
                         if !self.can_eq(param_env, ty, *prev_ty) {
                             if type_diffs.iter().any(|diff| {
-                                let Sorts(expected_found) = diff else {
+                                let TypeError::Sorts(expected_found) = diff else {
                                     return false;
                                 };
                                 self.can_eq(param_env, expected_found.found, ty)
@@ -4248,7 +4248,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let ocx = ObligationCtxt::new(self.infcx);
         let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
         for diff in type_diffs {
-            let Sorts(expected_found) = diff else {
+            let TypeError::Sorts(expected_found) = diff else {
                 continue;
             };
             let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else {
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index ce4fa5fa47c..4a935f4a64a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -32,6 +32,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_infer::infer::relate::MatchAgainstFreshVars;
+use rustc_infer::infer::relate::TypeRelation;
 use rustc_infer::infer::BoundRegionConversionTime;
 use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType;
 use rustc_infer::infer::DefineOpaqueTypes;
@@ -40,10 +42,9 @@ use rustc_middle::bug;
 use rustc_middle::dep_graph::dep_kinds;
 use rustc_middle::dep_graph::DepNodeIndex;
 use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::ty::_match::MatchAgainstFreshVars;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
+use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
-use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, PolyProjectionPredicate, Upcast};
 use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs
new file mode 100644
index 00000000000..27623ea9cac
--- /dev/null
+++ b/compiler/rustc_type_ir/src/error.rs
@@ -0,0 +1,106 @@
+use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
+
+use crate::solve::NoSolution;
+use crate::{self as ty, Interner};
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[derive(TypeFoldable_Generic, TypeVisitable_Generic)]
+pub struct ExpectedFound<T> {
+    pub expected: T,
+    pub found: T,
+}
+
+impl<T> ExpectedFound<T> {
+    pub fn new(a_is_expected: bool, a: T, b: T) -> Self {
+        if a_is_expected {
+            ExpectedFound { expected: a, found: b }
+        } else {
+            ExpectedFound { expected: b, found: a }
+        }
+    }
+}
+
+// Data structures used in type unification
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Copy(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = "")
+)]
+#[derive(TypeVisitable_Generic)]
+#[rustc_pass_by_value]
+pub enum TypeError<I: Interner> {
+    Mismatch,
+    ConstnessMismatch(ExpectedFound<ty::BoundConstness>),
+    PolarityMismatch(ExpectedFound<ty::PredicatePolarity>),
+    SafetyMismatch(ExpectedFound<I::Safety>),
+    AbiMismatch(ExpectedFound<I::Abi>),
+    Mutability,
+    ArgumentMutability(usize),
+    TupleSize(ExpectedFound<usize>),
+    FixedArraySize(ExpectedFound<u64>),
+    ArgCount,
+
+    RegionsDoesNotOutlive(I::Region, I::Region),
+    RegionsInsufficientlyPolymorphic(I::BoundRegion, I::Region),
+    RegionsPlaceholderMismatch,
+
+    Sorts(ExpectedFound<I::Ty>),
+    ArgumentSorts(ExpectedFound<I::Ty>, usize),
+    Traits(ExpectedFound<I::DefId>),
+    VariadicMismatch(ExpectedFound<bool>),
+
+    /// Instantiating a type variable with the given type would have
+    /// created a cycle (because it appears somewhere within that
+    /// type).
+    CyclicTy(I::Ty),
+    CyclicConst(I::Const),
+    ProjectionMismatched(ExpectedFound<I::DefId>),
+    ExistentialMismatch(ExpectedFound<I::BoundExistentialPredicates>),
+    ConstMismatch(ExpectedFound<I::Const>),
+
+    IntrinsicCast,
+    /// Safe `#[target_feature]` functions are not assignable to safe function pointers.
+    TargetFeatureCast(I::DefId),
+}
+
+impl<I: Interner> TypeError<I> {
+    pub fn involves_regions(self) -> bool {
+        match self {
+            TypeError::RegionsDoesNotOutlive(_, _)
+            | TypeError::RegionsInsufficientlyPolymorphic(_, _)
+            | TypeError::RegionsPlaceholderMismatch => true,
+            _ => false,
+        }
+    }
+
+    pub fn must_include_note(self) -> bool {
+        use self::TypeError::*;
+        match self {
+            CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_)
+            | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
+            | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false,
+
+            Mutability
+            | ArgumentMutability(_)
+            | TupleSize(_)
+            | ArgCount
+            | RegionsDoesNotOutlive(..)
+            | RegionsInsufficientlyPolymorphic(..)
+            | RegionsPlaceholderMismatch
+            | Traits(_)
+            | ProjectionMismatched(_)
+            | ExistentialMismatch(_)
+            | ConstMismatch(_)
+            | IntrinsicCast => true,
+        }
+    }
+}
+
+impl<I: Interner> From<TypeError<I>> for NoSolution {
+    fn from(_: TypeError<I>) -> NoSolution {
+        NoSolution
+    }
+}
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 94874a6acfc..205a1e5f100 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -7,7 +7,10 @@ use std::fmt::Debug;
 use std::hash::Hash;
 use std::ops::Deref;
 
+use rustc_ast_ir::Mutability;
+
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
+use crate::relate::Relate;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, CollectAndApply, DebugWithInfcx, Interner, UpcastFrom};
 
@@ -21,6 +24,7 @@ pub trait Ty<I: Interner<Ty = Self>>:
     + IntoKind<Kind = ty::TyKind<I>>
     + TypeSuperVisitable<I>
     + TypeSuperFoldable<I>
+    + Relate<I>
     + Flags
 {
     fn new_bool(interner: I) -> Self;
@@ -35,8 +39,37 @@ pub trait Ty<I: Interner<Ty = Self>>:
 
     fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self;
 
+    fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self;
+
+    fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self;
+
+    fn new_foreign(interner: I, def_id: I::DefId) -> Self;
+
+    fn new_dynamic(
+        interner: I,
+        preds: I::BoundExistentialPredicates,
+        region: I::Region,
+        kind: ty::DynKind,
+    ) -> Self;
+
     fn new_coroutine(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
 
+    fn new_coroutine_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_coroutine_witness(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_ptr(interner: I, ty: Self, mutbl: Mutability) -> Self;
+
+    fn new_ref(interner: I, region: I::Region, ty: Self, mutbl: Mutability) -> Self;
+
+    fn new_array_with_const_len(interner: I, ty: Self, len: I::Const) -> Self;
+
+    fn new_slice(interner: I, ty: Self) -> Self;
+
+    fn new_tup(interner: I, tys: &[I::Ty]) -> Self;
+
     fn new_tup_from_iter<It, T>(interner: I, iter: It) -> T::Output
     where
         It: Iterator<Item = T>,
@@ -49,6 +82,12 @@ pub trait Ty<I: Interner<Ty = Self>>:
     fn from_closure_kind(interner: I, kind: ty::ClosureKind) -> Self;
 
     fn from_coroutine_closure_kind(interner: I, kind: ty::ClosureKind) -> Self;
+
+    fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_fn_ptr(interner: I, sig: ty::Binder<I, ty::FnSig<I>>) -> Self;
+
+    fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self;
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
@@ -84,6 +123,7 @@ pub trait Region<I: Interner<Region = Self>>:
     + IntoKind<Kind = ty::RegionKind<I>>
     + Flags
     + TypeVisitable<I>
+    + Relate<I>
 {
     fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self;
 
@@ -102,8 +142,11 @@ pub trait Const<I: Interner<Const = Self>>:
     + IntoKind<Kind = ty::ConstKind<I>>
     + TypeSuperVisitable<I>
     + TypeSuperFoldable<I>
+    + Relate<I>
     + Flags
 {
+    fn try_to_target_usize(self, interner: I) -> Option<u64>;
+
     fn new_infer(interner: I, var: ty::InferConst) -> Self;
 
     fn new_var(interner: I, var: ty::ConstVid) -> Self;
@@ -113,6 +156,8 @@ pub trait Const<I: Interner<Const = Self>>:
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self;
+
+    fn new_expr(interner: I, expr: I::ExprConst) -> Self;
 }
 
 pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
@@ -128,13 +173,14 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
     + Deref<Target: Deref<Target = [I::GenericArg]>>
     + Default
     + TypeFoldable<I>
+    + Relate<I>
 {
     fn type_at(self, i: usize) -> I::Ty;
 
     fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs;
 
     fn extend_with_error(
-        tcx: I,
+        interner: I,
         def_id: I::DefId,
         original_args: &[I::GenericArg],
     ) -> I::GenericArgs;
@@ -193,3 +239,11 @@ pub trait BoundVarLike<I: Interner> {
 pub trait ParamLike {
     fn index(self) -> u32;
 }
+
+pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
+    fn def_id(self) -> I::DefId;
+}
+
+pub trait Features<I: Interner>: Copy {
+    fn generic_const_exprs(self) -> bool;
+}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 6ebb434299b..ad1d2753b28 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -6,6 +6,7 @@ use std::ops::Deref;
 use crate::fold::TypeFoldable;
 use crate::inherent::*;
 use crate::ir_print::IrPrint;
+use crate::relate::Relate;
 use crate::solve::inspect::CanonicalGoalEvaluationStep;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, DebugWithInfcx};
@@ -25,8 +26,8 @@ pub trait Interner:
     + IrPrint<ty::CoercePredicate<Self>>
     + IrPrint<ty::FnSig<Self>>
 {
-    type DefId: Copy + Debug + Hash + Eq + TypeVisitable<Self>;
-    type AdtDef: Copy + Debug + Hash + Eq;
+    type DefId: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type AdtDef: AdtDef<Self>;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref<Target = [Self::GenericArg]>;
@@ -35,8 +36,15 @@ pub trait Interner:
         + Hash
         + Eq
         + IntoKind<Kind = ty::GenericArgKind<Self>>
-        + TypeVisitable<Self>;
-    type Term: Copy + Debug + Hash + Eq + IntoKind<Kind = ty::TermKind<Self>> + TypeVisitable<Self>;
+        + TypeVisitable<Self>
+        + Relate<Self>;
+    type Term: Copy
+        + Debug
+        + Hash
+        + Eq
+        + IntoKind<Kind = ty::TermKind<Self>>
+        + TypeFoldable<Self>
+        + Relate<Self>;
 
     type BoundVarKinds: Copy
         + Debug
@@ -66,11 +74,11 @@ pub trait Interner:
 
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
-    type BoundExistentialPredicates: Copy + DebugWithInfcx<Self> + Hash + Eq;
+    type BoundExistentialPredicates: Copy + DebugWithInfcx<Self> + Hash + Eq + Relate<Self>;
     type AllocId: Copy + Debug + Hash + Eq;
-    type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx<Self>;
-    type Safety: Safety<Self>;
-    type Abi: Abi<Self>;
+    type Pat: Copy + Debug + Hash + Eq + DebugWithInfcx<Self> + Relate<Self>;
+    type Safety: Safety<Self> + TypeFoldable<Self> + Relate<Self>;
+    type Abi: Abi<Self> + TypeFoldable<Self> + Relate<Self>;
 
     // Kinds of consts
     type Const: Const<Self>;
@@ -78,7 +86,7 @@ pub trait Interner:
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
     type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
     type ValueConst: Copy + Debug + Hash + Eq;
-    type ExprConst: Copy + DebugWithInfcx<Self> + Hash + Eq;
+    type ExprConst: Copy + DebugWithInfcx<Self> + Hash + Eq + Relate<Self>;
 
     // Kinds of regions
     type Region: Region<Self>;
@@ -93,11 +101,16 @@ pub trait Interner:
     type Clause: Clause<Self>;
     type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
 
+    fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;
+
     fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
 
     type GenericsOf: GenericsOf<Self>;
     fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf;
 
+    type VariancesOf: Copy + Debug + Deref<Target = [ty::Variance]>;
+    fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf;
+
     // FIXME: Remove after uplifting `EarlyBinder`
     fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
 
@@ -112,7 +125,12 @@ pub trait Interner:
     ) -> (ty::TraitRef<Self>, Self::GenericArgsSlice);
 
     fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs;
-    fn mk_args_from_iter(self, args: impl Iterator<Item = Self::GenericArg>) -> Self::GenericArgs;
+
+    fn mk_args_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::GenericArg, Self::GenericArgs>;
+
     fn check_and_mk_args(
         self,
         def_id: Self::DefId,
@@ -124,9 +142,17 @@ pub trait Interner:
         step: CanonicalGoalEvaluationStep<Self>,
     ) -> Self::CanonicalGoalEvaluationStepRef;
 
+    fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
+    where
+        I: Iterator<Item = T>,
+        T: CollectAndApply<Self::Ty, Self::Tys>;
+
     fn parent(self, def_id: Self::DefId) -> Self::DefId;
 
     fn recursion_limit(self) -> usize;
+
+    type Features: Features<Self>;
+    fn features(self) -> Self::Features;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 217c056d0ba..73716468930 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -21,18 +21,20 @@ use std::hash::Hash;
 #[cfg(not(feature = "nightly"))]
 use std::sync::Arc as Lrc;
 
+// These modules are `pub` since they are not glob-imported.
 #[macro_use]
 pub mod visit;
 #[cfg(feature = "nightly")]
 pub mod codec;
+pub mod error;
 pub mod fold;
 pub mod inherent;
 pub mod ir_print;
 pub mod lift;
+pub mod relate;
 pub mod solve;
-pub mod ty_info;
-pub mod ty_kind;
 
+// These modules are not `pub` since they are glob-imported.
 #[macro_use]
 mod macros;
 mod binder;
@@ -46,6 +48,8 @@ mod interner;
 mod predicate;
 mod predicate_kind;
 mod region_kind;
+mod ty_info;
+mod ty_kind;
 mod upcast;
 
 pub use binder::*;
diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs
index f2f7b165de5..aae5aeb5fb3 100644
--- a/compiler/rustc_type_ir/src/macros.rs
+++ b/compiler/rustc_type_ir/src/macros.rs
@@ -48,10 +48,22 @@ TrivialTypeTraversalImpls! {
     u32,
     u64,
     String,
-    crate::DebruijnIndex,
     crate::AliasRelationDirection,
+    crate::AliasTyKind,
+    crate::BoundConstness,
+    crate::DebruijnIndex,
+    crate::FloatTy,
+    crate::InferTy,
+    crate::IntVarValue,
+    crate::PredicatePolarity,
+    crate::RegionVid,
+    crate::solve::BuiltinImplSource,
+    crate::solve::Certainty,
+    crate::solve::GoalSource,
+    crate::solve::MaybeCause,
+    crate::solve::NoSolution,
     crate::UniverseIndex,
-    rustc_ast_ir::Mutability,
+    crate::Variance,
     rustc_ast_ir::Movability,
-    crate::PredicatePolarity,
+    rustc_ast_ir::Mutability,
 }
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index 9e0e52cfb4b..63a4c2e9d1f 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -793,3 +793,36 @@ pub struct CoercePredicate<I: Interner> {
     pub a: I::Ty,
     pub b: I::Ty,
 }
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))]
+pub enum BoundConstness {
+    /// `Type: Trait`
+    NotConst,
+    /// `Type: const Trait`
+    Const,
+    /// `Type: ~const Trait`
+    ///
+    /// Requires resolving to const only when we are in a const context.
+    ConstIfConst,
+}
+
+impl BoundConstness {
+    pub fn as_str(self) -> &'static str {
+        match self {
+            Self::NotConst => "",
+            Self::Const => "const",
+            Self::ConstIfConst => "~const",
+        }
+    }
+}
+
+impl fmt::Display for BoundConstness {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::NotConst => f.write_str("normal"),
+            Self::Const => f.write_str("const"),
+            Self::ConstIfConst => f.write_str("~const"),
+        }
+    }
+}
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
new file mode 100644
index 00000000000..cae1d13020d
--- /dev/null
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -0,0 +1,666 @@
+use std::iter;
+
+use rustc_ast_ir::Mutability;
+use rustc_type_ir::error::{ExpectedFound, TypeError};
+use rustc_type_ir::fold::TypeFoldable;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Interner};
+use tracing::{debug, instrument};
+
+pub type RelateResult<I, T> = Result<T, TypeError<I>>;
+
+/// Extra information about why we ended up with a particular variance.
+/// This is only used to add more information to error messages, and
+/// has no effect on soundness. While choosing the 'wrong' `VarianceDiagInfo`
+/// may lead to confusing notes in error messages, it will never cause
+/// a miscompilation or unsoundness.
+///
+/// When in doubt, use `VarianceDiagInfo::default()`
+#[derive(derivative::Derivative)]
+#[derivative(
+    Copy(bound = ""),
+    Clone(bound = ""),
+    Debug(bound = ""),
+    Default(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = "")
+)]
+pub enum VarianceDiagInfo<I: Interner> {
+    /// No additional information - this is the default.
+    /// We will not add any additional information to error messages.
+    #[derivative(Default)]
+    None,
+    /// We switched our variance because a generic argument occurs inside
+    /// the invariant generic argument of another type.
+    Invariant {
+        /// The generic type containing the generic parameter
+        /// that changes the variance (e.g. `*mut T`, `MyStruct<T>`)
+        ty: I::Ty,
+        /// The index of the generic parameter being used
+        /// (e.g. `0` for `*mut T`, `1` for `MyStruct<'CovariantParam, 'InvariantParam>`)
+        param_index: u32,
+    },
+}
+
+impl<I: Interner> VarianceDiagInfo<I> {
+    /// Mirrors `Variance::xform` - used to 'combine' the existing
+    /// and new `VarianceDiagInfo`s when our variance changes.
+    pub fn xform(self, other: VarianceDiagInfo<I>) -> VarianceDiagInfo<I> {
+        // For now, just use the first `VarianceDiagInfo::Invariant` that we see
+        match self {
+            VarianceDiagInfo::None => other,
+            VarianceDiagInfo::Invariant { .. } => self,
+        }
+    }
+}
+
+pub trait TypeRelation<I: Interner>: Sized {
+    fn tcx(&self) -> I;
+
+    /// Returns a static string we can use for printouts.
+    fn tag(&self) -> &'static str;
+
+    /// Generic relation routine suitable for most anything.
+    fn relate<T: Relate<I>>(&mut self, a: T, b: T) -> RelateResult<I, T> {
+        Relate::relate(self, a, b)
+    }
+
+    /// Relate the two args for the given item. The default
+    /// is to look up the variance for the item and proceed
+    /// accordingly.
+    fn relate_item_args(
+        &mut self,
+        item_def_id: I::DefId,
+        a_arg: I::GenericArgs,
+        b_arg: I::GenericArgs,
+    ) -> RelateResult<I, I::GenericArgs> {
+        debug!(
+            "relate_item_args(item_def_id={:?}, a_arg={:?}, b_arg={:?})",
+            item_def_id, a_arg, b_arg
+        );
+
+        let tcx = self.tcx();
+        let opt_variances = tcx.variances_of(item_def_id);
+        relate_args_with_variances(self, item_def_id, &opt_variances, a_arg, b_arg, true)
+    }
+
+    /// Switch variance for the purpose of relating `a` and `b`.
+    fn relate_with_variance<T: Relate<I>>(
+        &mut self,
+        variance: ty::Variance,
+        info: VarianceDiagInfo<I>,
+        a: T,
+        b: T,
+    ) -> RelateResult<I, T>;
+
+    // Overridable relations. You shouldn't typically call these
+    // directly, instead call `relate()`, which in turn calls
+    // these. This is both more uniform but also allows us to add
+    // additional hooks for other types in the future if needed
+    // without making older code, which called `relate`, obsolete.
+
+    fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty>;
+
+    fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult<I, I::Region>;
+
+    fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const>;
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<I, T>,
+        b: ty::Binder<I, T>,
+    ) -> RelateResult<I, ty::Binder<I, T>>
+    where
+        T: Relate<I>;
+}
+
+pub trait Relate<I: Interner>: TypeFoldable<I> + PartialEq + Copy {
+    fn relate<R: TypeRelation<I>>(relation: &mut R, a: Self, b: Self) -> RelateResult<I, Self>;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Relate impls
+
+#[inline]
+pub fn relate_args_invariantly<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    a_arg: I::GenericArgs,
+    b_arg: I::GenericArgs,
+) -> RelateResult<I, I::GenericArgs> {
+    relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| {
+        relation.relate_with_variance(ty::Variance::Invariant, VarianceDiagInfo::default(), a, b)
+    }))
+}
+
+pub fn relate_args_with_variances<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    ty_def_id: I::DefId,
+    variances: &[ty::Variance],
+    a_arg: I::GenericArgs,
+    b_arg: I::GenericArgs,
+    fetch_ty_for_diag: bool,
+) -> RelateResult<I, I::GenericArgs> {
+    let tcx = relation.tcx();
+
+    let mut cached_ty = None;
+    let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| {
+        let variance = variances[i];
+        let variance_info = if variance == ty::Variance::Invariant && fetch_ty_for_diag {
+            let ty =
+                *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, &a_arg));
+            VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
+        } else {
+            VarianceDiagInfo::default()
+        };
+        relation.relate_with_variance(variance, variance_info, a, b)
+    });
+
+    tcx.mk_args_from_iter(params)
+}
+
+impl<I: Interner> Relate<I> for ty::FnSig<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::FnSig<I>,
+        b: ty::FnSig<I>,
+    ) -> RelateResult<I, ty::FnSig<I>> {
+        let tcx = relation.tcx();
+
+        if a.c_variadic != b.c_variadic {
+            return Err(TypeError::VariadicMismatch({
+                let a = a.c_variadic;
+                let b = b.c_variadic;
+                ExpectedFound::new(true, a, b)
+            }));
+        }
+        let safety = relation.relate(a.safety, b.safety)?;
+        let abi = relation.relate(a.abi, b.abi)?;
+
+        let a_inputs = a.inputs();
+        let b_inputs = b.inputs();
+
+        if a_inputs.len() != b_inputs.len() {
+            return Err(TypeError::ArgCount);
+        }
+
+        let inputs_and_output = iter::zip(a_inputs.iter(), b_inputs.iter())
+            .map(|(&a, &b)| ((a, b), false))
+            .chain(iter::once(((a.output(), b.output()), true)))
+            .map(|((a, b), is_output)| {
+                if is_output {
+                    relation.relate(a, b)
+                } else {
+                    relation.relate_with_variance(
+                        ty::Variance::Contravariant,
+                        VarianceDiagInfo::default(),
+                        a,
+                        b,
+                    )
+                }
+            })
+            .enumerate()
+            .map(|(i, r)| match r {
+                Err(TypeError::Sorts(exp_found) | TypeError::ArgumentSorts(exp_found, _)) => {
+                    Err(TypeError::ArgumentSorts(exp_found, i))
+                }
+                Err(TypeError::Mutability | TypeError::ArgumentMutability(_)) => {
+                    Err(TypeError::ArgumentMutability(i))
+                }
+                r => r,
+            });
+        Ok(ty::FnSig {
+            inputs_and_output: tcx.mk_type_list_from_iter(inputs_and_output)?,
+            c_variadic: a.c_variadic,
+            safety,
+            abi,
+        })
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::BoundConstness {
+    fn relate<R: TypeRelation<I>>(
+        _relation: &mut R,
+        a: ty::BoundConstness,
+        b: ty::BoundConstness,
+    ) -> RelateResult<I, ty::BoundConstness> {
+        if a != b {
+            Err(TypeError::ConstnessMismatch(ExpectedFound::new(true, a, b)))
+        } else {
+            Ok(a)
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::AliasTy<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::AliasTy<I>,
+        b: ty::AliasTy<I>,
+    ) -> RelateResult<I, ty::AliasTy<I>> {
+        if a.def_id != b.def_id {
+            Err(TypeError::ProjectionMismatched({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = match a.kind(relation.tcx()) {
+                ty::Opaque => relate_args_with_variances(
+                    relation,
+                    a.def_id,
+                    &relation.tcx().variances_of(a.def_id),
+                    a.args,
+                    b.args,
+                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
+                )?,
+                ty::Projection | ty::Weak | ty::Inherent => {
+                    relate_args_invariantly(relation, a.args, b.args)?
+                }
+            };
+            Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args))
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::AliasTerm<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::AliasTerm<I>,
+        b: ty::AliasTerm<I>,
+    ) -> RelateResult<I, ty::AliasTerm<I>> {
+        if a.def_id != b.def_id {
+            Err(TypeError::ProjectionMismatched({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = match a.kind(relation.tcx()) {
+                ty::AliasTermKind::OpaqueTy => relate_args_with_variances(
+                    relation,
+                    a.def_id,
+                    &relation.tcx().variances_of(a.def_id),
+                    a.args,
+                    b.args,
+                    false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
+                )?,
+                ty::AliasTermKind::ProjectionTy
+                | ty::AliasTermKind::WeakTy
+                | ty::AliasTermKind::InherentTy
+                | ty::AliasTermKind::UnevaluatedConst
+                | ty::AliasTermKind::ProjectionConst => {
+                    relate_args_invariantly(relation, a.args, b.args)?
+                }
+            };
+            Ok(ty::AliasTerm::new(relation.tcx(), a.def_id, args))
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::ExistentialProjection<I>,
+        b: ty::ExistentialProjection<I>,
+    ) -> RelateResult<I, ty::ExistentialProjection<I>> {
+        if a.def_id != b.def_id {
+            Err(TypeError::ProjectionMismatched({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let term = relation.relate_with_variance(
+                ty::Variance::Invariant,
+                VarianceDiagInfo::default(),
+                a.term,
+                b.term,
+            )?;
+            let args = relation.relate_with_variance(
+                ty::Variance::Invariant,
+                VarianceDiagInfo::default(),
+                a.args,
+                b.args,
+            )?;
+            Ok(ty::ExistentialProjection { def_id: a.def_id, args, term })
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::TraitRef<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::TraitRef<I>,
+        b: ty::TraitRef<I>,
+    ) -> RelateResult<I, ty::TraitRef<I>> {
+        // Different traits cannot be related.
+        if a.def_id != b.def_id {
+            Err(TypeError::Traits({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = relate_args_invariantly(relation, a.args, b.args)?;
+            Ok(ty::TraitRef::new(relation.tcx(), a.def_id, args))
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::ExistentialTraitRef<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::ExistentialTraitRef<I>,
+        b: ty::ExistentialTraitRef<I>,
+    ) -> RelateResult<I, ty::ExistentialTraitRef<I>> {
+        // Different traits cannot be related.
+        if a.def_id != b.def_id {
+            Err(TypeError::Traits({
+                let a = a.def_id;
+                let b = b.def_id;
+                ExpectedFound::new(true, a, b)
+            }))
+        } else {
+            let args = relate_args_invariantly(relation, a.args, b.args)?;
+            Ok(ty::ExistentialTraitRef { def_id: a.def_id, args })
+        }
+    }
+}
+
+/// Relates `a` and `b` structurally, calling the relation for all nested values.
+/// Any semantic equality, e.g. of projections, and inference variables have to be
+/// handled by the caller.
+#[instrument(level = "trace", skip(relation), ret)]
+pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    a: I::Ty,
+    b: I::Ty,
+) -> RelateResult<I, I::Ty> {
+    let tcx = relation.tcx();
+    match (a.kind(), b.kind()) {
+        (ty::Infer(_), _) | (_, ty::Infer(_)) => {
+            // The caller should handle these cases!
+            panic!("var types encountered in structurally_relate_tys")
+        }
+
+        (ty::Bound(..), _) | (_, ty::Bound(..)) => {
+            panic!("bound types encountered in structurally_relate_tys")
+        }
+
+        (ty::Error(guar), _) | (_, ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)),
+
+        (ty::Never, _)
+        | (ty::Char, _)
+        | (ty::Bool, _)
+        | (ty::Int(_), _)
+        | (ty::Uint(_), _)
+        | (ty::Float(_), _)
+        | (ty::Str, _)
+            if a == b =>
+        {
+            Ok(a)
+        }
+
+        (ty::Param(a_p), ty::Param(b_p)) if a_p.index() == b_p.index() => {
+            // FIXME: Put this back
+            //debug_assert_eq!(a_p.name(), b_p.name(), "param types with same index differ in name");
+            Ok(a)
+        }
+
+        (ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a),
+
+        (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args)) if a_def == b_def => {
+            let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?;
+            Ok(Ty::new_adt(tcx, a_def, args))
+        }
+
+        (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)),
+
+        (ty::Dynamic(a_obj, a_region, a_repr), ty::Dynamic(b_obj, b_region, b_repr))
+            if a_repr == b_repr =>
+        {
+            Ok(Ty::new_dynamic(
+                tcx,
+                relation.relate(a_obj, b_obj)?,
+                relation.relate(a_region, b_region)?,
+                a_repr,
+            ))
+        }
+
+        (ty::Coroutine(a_id, a_args), ty::Coroutine(b_id, b_args)) if a_id == b_id => {
+            // All Coroutine types with the same id represent
+            // the (anonymous) type of the same coroutine expression. So
+            // all of their regions should be equated.
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_coroutine(tcx, a_id, args))
+        }
+
+        (ty::CoroutineWitness(a_id, a_args), ty::CoroutineWitness(b_id, b_args))
+            if a_id == b_id =>
+        {
+            // All CoroutineWitness types with the same id represent
+            // the (anonymous) type of the same coroutine expression. So
+            // all of their regions should be equated.
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_coroutine_witness(tcx, a_id, args))
+        }
+
+        (ty::Closure(a_id, a_args), ty::Closure(b_id, b_args)) if a_id == b_id => {
+            // All Closure types with the same id represent
+            // the (anonymous) type of the same closure expression. So
+            // all of their regions should be equated.
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_closure(tcx, a_id, args))
+        }
+
+        (ty::CoroutineClosure(a_id, a_args), ty::CoroutineClosure(b_id, b_args))
+            if a_id == b_id =>
+        {
+            let args = relate_args_invariantly(relation, a_args, b_args)?;
+            Ok(Ty::new_coroutine_closure(tcx, a_id, args))
+        }
+
+        (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => {
+            if a_mutbl != b_mutbl {
+                return Err(TypeError::Mutability);
+            }
+
+            let (variance, info) = match a_mutbl {
+                Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None),
+                Mutability::Mut => {
+                    (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
+                }
+            };
+
+            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
+
+            Ok(Ty::new_ptr(tcx, ty, a_mutbl))
+        }
+
+        (ty::Ref(a_r, a_ty, a_mutbl), ty::Ref(b_r, b_ty, b_mutbl)) => {
+            if a_mutbl != b_mutbl {
+                return Err(TypeError::Mutability);
+            }
+
+            let (variance, info) = match a_mutbl {
+                Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None),
+                Mutability::Mut => {
+                    (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
+                }
+            };
+
+            let r = relation.relate(a_r, b_r)?;
+            let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?;
+
+            Ok(Ty::new_ref(tcx, r, ty, a_mutbl))
+        }
+
+        (ty::Array(a_t, sz_a), ty::Array(b_t, sz_b)) => {
+            let t = relation.relate(a_t, b_t)?;
+            match relation.relate(sz_a, sz_b) {
+                Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)),
+                Err(err) => {
+                    // Check whether the lengths are both concrete/known values,
+                    // but are unequal, for better diagnostics.
+                    let sz_a = sz_a.try_to_target_usize(tcx);
+                    let sz_b = sz_b.try_to_target_usize(tcx);
+
+                    match (sz_a, sz_b) {
+                        (Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => Err(
+                            TypeError::FixedArraySize(ExpectedFound::new(true, sz_a_val, sz_b_val)),
+                        ),
+                        _ => Err(err),
+                    }
+                }
+            }
+        }
+
+        (ty::Slice(a_t), ty::Slice(b_t)) => {
+            let t = relation.relate(a_t, b_t)?;
+            Ok(Ty::new_slice(tcx, t))
+        }
+
+        (ty::Tuple(as_), ty::Tuple(bs)) => {
+            if as_.len() == bs.len() {
+                Ok(Ty::new_tup_from_iter(
+                    tcx,
+                    iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)),
+                )?)
+            } else if !(as_.is_empty() || bs.is_empty()) {
+                Err(TypeError::TupleSize(ExpectedFound::new(true, as_.len(), bs.len())))
+            } else {
+                Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
+            }
+        }
+
+        (ty::FnDef(a_def_id, a_args), ty::FnDef(b_def_id, b_args)) if a_def_id == b_def_id => {
+            let args = relation.relate_item_args(a_def_id, a_args, b_args)?;
+            Ok(Ty::new_fn_def(tcx, a_def_id, args))
+        }
+
+        (ty::FnPtr(a_fty), ty::FnPtr(b_fty)) => {
+            let fty = relation.relate(a_fty, b_fty)?;
+            Ok(Ty::new_fn_ptr(tcx, fty))
+        }
+
+        // Alias tend to mostly already be handled downstream due to normalization.
+        (ty::Alias(a_kind, a_data), ty::Alias(b_kind, b_data)) => {
+            let alias_ty = relation.relate(a_data, b_data)?;
+            assert_eq!(a_kind, b_kind);
+            Ok(Ty::new_alias(tcx, a_kind, alias_ty))
+        }
+
+        (ty::Pat(a_ty, a_pat), ty::Pat(b_ty, b_pat)) => {
+            let ty = relation.relate(a_ty, b_ty)?;
+            let pat = relation.relate(a_pat, b_pat)?;
+            Ok(Ty::new_pat(tcx, ty, pat))
+        }
+
+        _ => Err(TypeError::Sorts(ExpectedFound::new(true, a, b))),
+    }
+}
+
+/// Relates `a` and `b` structurally, calling the relation for all nested values.
+/// Any semantic equality, e.g. of unevaluated consts, and inference variables have
+/// to be handled by the caller.
+///
+/// FIXME: This is not totally structual, which probably should be fixed.
+/// See the HACKs below.
+pub fn structurally_relate_consts<I: Interner, R: TypeRelation<I>>(
+    relation: &mut R,
+    mut a: I::Const,
+    mut b: I::Const,
+) -> RelateResult<I, I::Const> {
+    debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
+    let tcx = relation.tcx();
+
+    if tcx.features().generic_const_exprs() {
+        a = tcx.expand_abstract_consts(a);
+        b = tcx.expand_abstract_consts(b);
+    }
+
+    debug!("{}.structurally_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
+
+    // Currently, the values that can be unified are primitive types,
+    // and those that derive both `PartialEq` and `Eq`, corresponding
+    // to structural-match types.
+    let is_match = match (a.kind(), b.kind()) {
+        (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
+            // The caller should handle these cases!
+            panic!("var types encountered in structurally_relate_consts: {:?} {:?}", a, b)
+        }
+
+        (ty::ConstKind::Error(_), _) => return Ok(a),
+        (_, ty::ConstKind::Error(_)) => return Ok(b),
+
+        (ty::ConstKind::Param(a_p), ty::ConstKind::Param(b_p)) if a_p.index() == b_p.index() => {
+            // FIXME: Put this back
+            // debug_assert_eq!(a_p.name, b_p.name, "param types with same index differ in name");
+            true
+        }
+        (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
+        (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val,
+
+        // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
+        // and is the better alternative to waiting until `generic_const_exprs` can
+        // be stabilized.
+        (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if au.def == bu.def => {
+            if cfg!(debug_assertions) {
+                let a_ty = tcx.type_of(au.def).instantiate(tcx, &au.args);
+                let b_ty = tcx.type_of(bu.def).instantiate(tcx, &bu.args);
+                assert_eq!(a_ty, b_ty);
+            }
+
+            let args = relation.relate_with_variance(
+                ty::Variance::Invariant,
+                VarianceDiagInfo::default(),
+                au.args,
+                bu.args,
+            )?;
+            return Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst { def: au.def, args }));
+        }
+        (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => {
+            let expr = relation.relate(ae, be)?;
+            return Ok(Const::new_expr(tcx, expr));
+        }
+        _ => false,
+    };
+    if is_match { Ok(a) } else { Err(TypeError::ConstMismatch(ExpectedFound::new(true, a, b))) }
+}
+
+impl<I: Interner, T: Relate<I>> Relate<I> for ty::Binder<I, T> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::Binder<I, T>,
+        b: ty::Binder<I, T>,
+    ) -> RelateResult<I, ty::Binder<I, T>> {
+        relation.binders(a, b)
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::PredicatePolarity {
+    fn relate<R: TypeRelation<I>>(
+        _relation: &mut R,
+        a: ty::PredicatePolarity,
+        b: ty::PredicatePolarity,
+    ) -> RelateResult<I, ty::PredicatePolarity> {
+        if a != b {
+            Err(TypeError::PolarityMismatch(ExpectedFound::new(true, a, b)))
+        } else {
+            Ok(a)
+        }
+    }
+}
+
+impl<I: Interner> Relate<I> for ty::TraitPredicate<I> {
+    fn relate<R: TypeRelation<I>>(
+        relation: &mut R,
+        a: ty::TraitPredicate<I>,
+        b: ty::TraitPredicate<I>,
+    ) -> RelateResult<I, ty::TraitPredicate<I>> {
+        Ok(ty::TraitPredicate {
+            trait_ref: relation.relate(a.trait_ref, b.trait_ref)?,
+            polarity: relation.relate(a.polarity, b.polarity)?,
+        })
+    }
+}
diff --git a/compiler/rustc_type_ir/src/solve.rs b/compiler/rustc_type_ir/src/solve.rs
index 3c24e851d7b..45125fe6191 100644
--- a/compiler/rustc_type_ir/src/solve.rs
+++ b/compiler/rustc_type_ir/src/solve.rs
@@ -20,7 +20,6 @@ pub type CanonicalResponse<I> = Canonical<I, Response<I>>;
 pub type QueryResult<I> = Result<CanonicalResponse<I>, NoSolution>;
 
 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-#[derive(TypeFoldable_Generic, TypeVisitable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub struct NoSolution;
 
@@ -60,7 +59,7 @@ impl<I: Interner, P> Goal<I, P> {
 ///
 /// This is necessary as we treat nested goals different depending on
 /// their source.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeVisitable_Generic, TypeFoldable_Generic)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub enum GoalSource {
     Misc,
@@ -170,7 +169,6 @@ pub enum CandidateSource<I: Interner> {
 }
 
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
-#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))]
 pub enum BuiltinImplSource {
     /// Some builtin impl we don't need to differentiate. This should be used
@@ -214,7 +212,6 @@ pub struct Response<I: Interner> {
 }
 
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
-#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub enum Certainty {
     Yes,
@@ -252,7 +249,6 @@ impl Certainty {
 
 /// Why we failed to evaluate a goal.
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
-#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub enum MaybeCause {
     /// We failed due to ambiguity. This ambiguity can either
diff --git a/config.example.toml b/config.example.toml
index 61655dad638..fb45493ec4a 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -389,8 +389,8 @@
 # a Nix toolchain on non-NixOS distributions.
 #patch-binaries-for-nix = false
 
-# Collect information and statistics about the current build and writes it to
-# disk. Enabling this or not has no impact on the resulting build output. The
+# Collect information and statistics about the current build, and write it to
+# disk. Enabling this has no impact on the resulting build output. The
 # schema of the file generated by the build metrics feature is unstable, and
 # this is not intended to be used during local development.
 #metrics = false
diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs
index 0b04fb4a274..a391141827e 100644
--- a/library/alloc/src/collections/binary_heap/mod.rs
+++ b/library/alloc/src/collections/binary_heap/mod.rs
@@ -1216,7 +1216,6 @@ impl<T, A: Allocator> BinaryHeap<T, A> {
     /// Basic usage:
     ///
     /// ```
-    /// #![feature(binary_heap_as_slice)]
     /// use std::collections::BinaryHeap;
     /// use std::io::{self, Write};
     ///
@@ -1225,7 +1224,7 @@ impl<T, A: Allocator> BinaryHeap<T, A> {
     /// io::sink().write(heap.as_slice()).unwrap();
     /// ```
     #[must_use]
-    #[unstable(feature = "binary_heap_as_slice", issue = "83659")]
+    #[stable(feature = "binary_heap_as_slice", since = "CURRENT_RUSTC_VERSION")]
     pub fn as_slice(&self) -> &[T] {
         self.data.as_slice()
     }
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 4ac0c9b15be..4749b8880fb 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -121,7 +121,6 @@
 #![feature(deref_pure_trait)]
 #![feature(dispatch_from_dyn)]
 #![feature(error_generic_member_access)]
-#![feature(error_in_core)]
 #![feature(exact_size_is_empty)]
 #![feature(extend_one)]
 #![feature(fmt_internals)]
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index 8451be63c2b..89538f272f0 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -24,7 +24,6 @@
 #![feature(binary_heap_into_iter_sorted)]
 #![feature(binary_heap_drain_sorted)]
 #![feature(slice_ptr_get)]
-#![feature(binary_heap_as_slice)]
 #![feature(inplace_iteration)]
 #![feature(iter_advance_by)]
 #![feature(iter_next_chunk)]
diff --git a/library/backtrace b/library/backtrace
-Subproject 5e05efa87905fb5b351a2bc5644d60c57d6d932
+Subproject 72265bea210891ae47bbe6d4f17b493ef060661
diff --git a/library/core/src/error.rs b/library/core/src/error.rs
index a3f2b767054..da18fdc6e1d 100644
--- a/library/core/src/error.rs
+++ b/library/core/src/error.rs
@@ -1,5 +1,5 @@
 #![doc = include_str!("error.md")]
-#![unstable(feature = "error_in_core", issue = "103765")]
+#![stable(feature = "error_in_core", since = "CURRENT_RUSTC_VERSION")]
 
 #[cfg(test)]
 mod tests;
@@ -130,7 +130,6 @@ pub trait Error: Debug + Display {
     ///
     /// ```rust
     /// #![feature(error_generic_member_access)]
-    /// #![feature(error_in_core)]
     /// use core::fmt;
     /// use core::error::{request_ref, Request};
     ///
@@ -361,8 +360,7 @@ impl dyn Error {
 /// Get a string value from an error.
 ///
 /// ```rust
-/// # #![feature(error_generic_member_access)]
-/// # #![feature(error_in_core)]
+/// #![feature(error_generic_member_access)]
 /// use std::error::Error;
 /// use core::error::request_value;
 ///
@@ -385,8 +383,7 @@ where
 /// Get a string reference from an error.
 ///
 /// ```rust
-/// # #![feature(error_generic_member_access)]
-/// # #![feature(error_in_core)]
+/// #![feature(error_generic_member_access)]
 /// use core::error::Error;
 /// use core::error::request_ref;
 ///
@@ -458,7 +455,6 @@ where
 ///
 /// ```
 /// #![feature(error_generic_member_access)]
-/// #![feature(error_in_core)]
 /// use core::fmt;
 /// use core::error::Request;
 /// use core::error::request_ref;
@@ -529,7 +525,6 @@ impl<'a> Request<'a> {
     ///
     /// ```rust
     /// #![feature(error_generic_member_access)]
-    /// #![feature(error_in_core)]
     ///
     /// use core::error::Request;
     ///
@@ -564,7 +559,6 @@ impl<'a> Request<'a> {
     ///
     /// ```rust
     /// #![feature(error_generic_member_access)]
-    /// #![feature(error_in_core)]
     ///
     /// use core::error::Request;
     ///
@@ -600,7 +594,6 @@ impl<'a> Request<'a> {
     ///
     /// ```rust
     /// #![feature(error_generic_member_access)]
-    /// #![feature(error_in_core)]
     ///
     /// use core::error::Request;
     ///
@@ -633,7 +626,6 @@ impl<'a> Request<'a> {
     ///
     /// ```rust
     /// #![feature(error_generic_member_access)]
-    /// #![feature(error_in_core)]
     ///
     /// use core::error::Request;
     ///
@@ -700,7 +692,6 @@ impl<'a> Request<'a> {
     ///
     /// ```rust
     /// #![feature(error_generic_member_access)]
-    /// #![feature(error_in_core)]
     ///
     /// use core::error::Request;
     /// use core::error::request_value;
@@ -788,7 +779,6 @@ impl<'a> Request<'a> {
     ///
     /// ```rust
     /// #![feature(error_generic_member_access)]
-    /// #![feature(error_in_core)]
     ///
     /// use core::error::Request;
     /// use core::error::request_ref;
diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs
index ab2158394bf..3a5a5af8bf5 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -212,6 +212,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
 
 macro_rules! impl_Display {
     ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
+        #[cfg(not(feature = "optimize_for_size"))]
         fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             // 2^128 is about 3*10^38, so 39 gives an extra byte of space
             let mut buf = [MaybeUninit::<u8>::uninit(); 39];
@@ -277,6 +278,38 @@ macro_rules! impl_Display {
             f.pad_integral(is_nonnegative, "", buf_slice)
         }
 
+        #[cfg(feature = "optimize_for_size")]
+        fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            // 2^128 is about 3*10^38, so 39 gives an extra byte of space
+            let mut buf = [MaybeUninit::<u8>::uninit(); 39];
+            let mut curr = buf.len();
+            let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
+
+            // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
+            // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
+            // each step this is kept the same as `n` is divided. Since `n` is always
+            // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
+            // is safe to access.
+            unsafe {
+                loop {
+                    curr -= 1;
+                    buf_ptr.add(curr).write((n % 10) as u8 + b'0');
+                    n /= 10;
+
+                    if n == 0 {
+                        break;
+                    }
+                }
+            }
+
+            // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
+            let buf_slice = unsafe {
+                str::from_utf8_unchecked(
+                    slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
+            };
+            f.pad_integral(is_nonnegative, "", buf_slice)
+        }
+
         $(#[stable(feature = "rust1", since = "1.0.0")]
         impl fmt::Display for $t {
             #[allow(unused_comparisons)]
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index 9a527073602..e253cfd2822 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -1708,8 +1708,6 @@ impl<T> Option<T> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(option_take_if)]
-    ///
     /// let mut x = Some(42);
     ///
     /// let prev = x.take_if(|v| if *v == 42 {
@@ -1726,7 +1724,7 @@ impl<T> Option<T> {
     /// assert_eq!(prev, Some(43));
     /// ```
     #[inline]
-    #[unstable(feature = "option_take_if", issue = "98934")]
+    #[stable(feature = "option_take_if", since = "CURRENT_RUSTC_VERSION")]
     pub fn take_if<P>(&mut self, predicate: P) -> Option<T>
     where
         P: FnOnce(&mut T) -> bool,
diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs
index d8fc3b7177f..0d2aa3070a1 100644
--- a/library/core/src/pin.rs
+++ b/library/core/src/pin.rs
@@ -184,7 +184,7 @@
 //! requires at least a level of pointer indirection each time a new object is added to the mix
 //! (and, practically, a heap allocation).
 //!
-//! Although there were other reason as well, this issue of expensive composition is the key thing
+//! Although there were other reasons as well, this issue of expensive composition is the key thing
 //! that drove Rust towards adopting a different model. It is particularly a problem
 //! when one considers, for example, the implications of composing together the [`Future`]s which
 //! will eventually make up an asynchronous task (including address-sensitive `async fn` state
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 20ff6fd7687..f632883b563 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -112,7 +112,6 @@
 #![feature(const_slice_from_ref)]
 #![feature(waker_getters)]
 #![feature(error_generic_member_access)]
-#![feature(error_in_core)]
 #![feature(trait_upcasting)]
 #![feature(is_ascii_octdigit)]
 #![feature(get_many_mut)]
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 79a504c5a5e..68bba5c2be1 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -34,10 +34,10 @@ addr2line = { version = "0.22.0", optional = true, default-features = false }
 libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true }
 
 [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies]
-object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] }
+object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] }
 
 [target.'cfg(target_os = "aix")'.dependencies]
-object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] }
+object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] }
 
 [dev-dependencies]
 rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index 9dd3d7d3fa1..f6b9de26c1c 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -533,6 +533,25 @@ impl OsString {
         unsafe { Box::from_raw(rw) }
     }
 
+    /// Consumes and leaks the `OsString`, returning a mutable reference to the contents,
+    /// `&'a mut OsStr`.
+    ///
+    /// The caller has free choice over the returned lifetime, including 'static.
+    /// Indeed, this function is ideally used for data that lives for the remainder of
+    /// the program’s life, as dropping the returned reference will cause a memory leak.
+    ///
+    /// It does not reallocate or shrink the `OsString`, so the leaked allocation may include
+    /// unused capacity that is not part of the returned slice. If you want to discard excess
+    /// capacity, call [`into_boxed_os_str`], and then [`Box::leak`] instead.
+    /// However, keep in mind that trimming the capacity may result in a reallocation and copy.
+    ///
+    /// [`into_boxed_os_str`]: Self::into_boxed_os_str
+    #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")]
+    #[inline]
+    pub fn leak<'a>(self) -> &'a mut OsStr {
+        OsStr::from_inner_mut(self.inner.leak())
+    }
+
     /// Part of a hack to make PathBuf::push/pop more efficient.
     #[inline]
     pub(crate) fn as_mut_vec_for_path_buf(&mut self) -> &mut Vec<u8> {
diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs
index b020e05eaab..5b39b9e34d8 100644
--- a/library/std/src/ffi/os_str/tests.rs
+++ b/library/std/src/ffi/os_str/tests.rs
@@ -24,6 +24,15 @@ fn test_os_string_clear() {
 }
 
 #[test]
+fn test_os_string_leak() {
+    let os_string = OsString::from("have a cake");
+    let (len, cap) = (os_string.len(), os_string.capacity());
+    let leaked = os_string.leak();
+    assert_eq!(leaked.as_encoded_bytes(), b"have a cake");
+    unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) }
+}
+
+#[test]
 fn test_os_string_capacity() {
     let os_string = OsString::with_capacity(0);
     assert_eq!(0, os_string.capacity());
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 9d6576fa841..1c226f9f08f 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -324,7 +324,6 @@
 #![feature(core_io_borrowed_buf)]
 #![feature(duration_constants)]
 #![feature(error_generic_member_access)]
-#![feature(error_in_core)]
 #![feature(error_iter)]
 #![feature(exact_size_is_empty)]
 #![feature(exclusive_wrapper)]
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index f4e1e2a38c1..adbcdcd2e67 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -1226,6 +1226,25 @@ impl PathBuf {
         self
     }
 
+    /// Consumes and leaks the `PathBuf`, returning a mutable reference to the contents,
+    /// `&'a mut Path`.
+    ///
+    /// The caller has free choice over the returned lifetime, including 'static.
+    /// Indeed, this function is ideally used for data that lives for the remainder of
+    /// the program’s life, as dropping the returned reference will cause a memory leak.
+    ///
+    /// It does not reallocate or shrink the `PathBuf`, so the leaked allocation may include
+    /// unused capacity that is not part of the returned slice. If you want to discard excess
+    /// capacity, call [`into_boxed_path`], and then [`Box::leak`] instead.
+    /// However, keep in mind that trimming the capacity may result in a reallocation and copy.
+    ///
+    /// [`into_boxed_path`]: Self::into_boxed_path
+    #[unstable(feature = "os_string_pathbuf_leak", issue = "125965")]
+    #[inline]
+    pub fn leak<'a>(self) -> &'a mut Path {
+        Path::from_inner_mut(self.inner.leak())
+    }
+
     /// Extends `self` with `path`.
     ///
     /// If `path` is absolute, it replaces the current path.
diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index 2d8e50d1f88..92702b395df 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -127,6 +127,16 @@ fn into() {
 }
 
 #[test]
+fn test_pathbuf_leak() {
+    let string = "/have/a/cake".to_owned();
+    let (len, cap) = (string.len(), string.capacity());
+    let buf = PathBuf::from(string);
+    let leaked = buf.leak();
+    assert_eq!(leaked.as_os_str().as_encoded_bytes(), b"/have/a/cake");
+    unsafe { drop(String::from_raw_parts(leaked.as_mut_os_str() as *mut OsStr as _, len, cap)) }
+}
+
+#[test]
 #[cfg(unix)]
 pub fn test_decompositions_unix() {
     t!("",
diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs
index 18b969bca85..f7c6b0877aa 100644
--- a/library/std/src/sys/os_str/bytes.rs
+++ b/library/std/src/sys/os_str/bytes.rs
@@ -177,6 +177,11 @@ impl Buf {
     }
 
     #[inline]
+    pub fn leak<'a>(self) -> &'a mut Slice {
+        unsafe { mem::transmute(self.inner.leak()) }
+    }
+
+    #[inline]
     pub fn into_box(self) -> Box<Slice> {
         unsafe { mem::transmute(self.inner.into_boxed_slice()) }
     }
diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs
index b3ceb55802d..dfff4dd4fb0 100644
--- a/library/std/src/sys/os_str/wtf8.rs
+++ b/library/std/src/sys/os_str/wtf8.rs
@@ -139,6 +139,11 @@ impl Buf {
     }
 
     #[inline]
+    pub fn leak<'a>(self) -> &'a mut Slice {
+        unsafe { mem::transmute(self.inner.leak()) }
+    }
+
+    #[inline]
     pub fn into_box(self) -> Box<Slice> {
         unsafe { mem::transmute(self.inner.into_box()) }
     }
diff --git a/library/std/src/sys/pal/windows/c/README.md b/library/std/src/sys/pal/windows/c/README.md
index d458e55efbc..efefc5faba7 100644
--- a/library/std/src/sys/pal/windows/c/README.md
+++ b/library/std/src/sys/pal/windows/c/README.md
@@ -3,7 +3,7 @@ be edited manually.
 
 To add bindings, edit `bindings.txt` then regenerate using the following command:
 
-    ./x run generate-windows-sys && ./x fmt library/std
+    ./x run generate-windows-sys && ./x fmt
 
 If you need to override generated functions or types then add them to
 `library/std/src/sys/pal/windows/c.rs`.
diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs
index 38e15f9f549..bb1e505285b 100644
--- a/library/std/src/sys_common/wtf8.rs
+++ b/library/std/src/sys_common/wtf8.rs
@@ -325,6 +325,11 @@ impl Wtf8Buf {
         self.bytes.shrink_to(min_capacity)
     }
 
+    #[inline]
+    pub fn leak<'a>(self) -> &'a mut Wtf8 {
+        unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) }
+    }
+
     /// Returns the number of bytes that this string buffer can hold without reallocating.
     #[inline]
     pub fn capacity(&self) -> usize {
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index fc433bc5843..cab37e0da47 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -59,17 +59,18 @@ check-aux:
 		library/alloc \
 		--no-doc
 	# Some doctests have intentional memory leaks.
+	# Some use file system operations to demonstrate dealing with `Result`.
 	$(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \
 		$(BOOTSTRAP) miri --stage 2 \
 		library/core \
 		library/alloc \
 		--doc
-	# In `std` we cannot test everything.
-	$(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \
+	# In `std` we cannot test everything, so we skip some modules.
+	$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
 		$(BOOTSTRAP) miri --stage 2 library/std \
 		--no-doc -- \
 		--skip fs:: --skip net:: --skip process:: --skip sys::pal::
-	$(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \
+	$(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \
 		$(BOOTSTRAP) miri --stage 2 library/std \
 		--doc -- \
 		--skip fs:: --skip net:: --skip process:: --skip sys::pal::
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 1b55fc85da4..9df4698f21f 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -9,7 +9,6 @@ use crate::core::builder;
 use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::channel::GitInfo;
-use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::output;
 use crate::utils::helpers::{add_dylib_path, exe, t};
 use crate::Compiler;
@@ -110,9 +109,8 @@ impl Step for ToolBuild {
             &self.target,
         );
 
-        let mut cargo = Command::from(cargo);
         // we check this below
-        let build_success = builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure());
+        let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {});
 
         builder.save_toolstate(
             tool,
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index d0da7d30660..0d9c21c4487 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -61,8 +61,8 @@ ENV SCRIPT python3 ../x.py check --stage 0 --set build.optimized-compiler-builti
            /scripts/validate-toolstate.sh && \
            /scripts/validate-error-codes.sh && \
            reuse --include-submodules lint && \
-           # Runs checks to ensure that there are no ES5 issues in our JS code.
-           es-check es8 ../src/librustdoc/html/static/js/*.js && \
+           # Runs checks to ensure that there are no issues in our JS code.
+           es-check es2019 ../src/librustdoc/html/static/js/*.js && \
            eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \
            eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \
            eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile
index c8c754914aa..d228dfc87eb 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile
@@ -1,3 +1,6 @@
+# This job builds a toolchain capable of building Fuchsia, and then builds
+# Fuchsia. See the build-fuchsia.sh script in this directory for more details.
+
 FROM ubuntu:22.04
 
 ARG DEBIAN_FRONTEND=noninteractive
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
index 9cc508fe928..913a0b0c09c 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
@@ -2,50 +2,87 @@
 
 # Downloads and builds the Fuchsia operating system using a toolchain installed
 # in $RUST_INSTALL_DIR.
+#
+# You may run this script locally using Docker with the following command:
+#
+# $ src/ci/docker/run.sh x86_64-gnu-integration
+#
+# Alternatively, from within the container with --dev, assuming you have made it
+# as far as building the toolchain with the above command:
+#
+# $ src/ci/docker/run.sh --dev x86_64-gnu-integration
+# docker# git config --global --add safe.directory /checkout/obj/fuchsia
+# docker# ../src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
+#
+# Also see the docs in the rustc-dev-guide for more info:
+# https://github.com/rust-lang/rustc-dev-guide/pull/1989
 
 set -euf -o pipefail
 
-INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6
+# Set this variable to 1 to disable updating the Fuchsia checkout. This is
+# useful for making local changes. You can find the Fuchsia checkout in
+# `obj/x86_64-gnu-integration/fuchsia` in your local checkout after running this
+# job for the first time.
+KEEP_CHECKOUT=
+
+# Any upstream refs that should be cherry-picked. This can be used to include
+# Gerrit changes from https://fxrev.dev during development (click the "Download"
+# button on a changelist to see the cherry pick ref). Example:
+# PICK_REFS=(refs/changes/71/1054071/2 refs/changes/74/1054574/2)
 PICK_REFS=()
 
+# The commit hash of Fuchsia's integration.git to check out. This controls the
+# commit hash of fuchsia.git and some other repos in the "monorepo" checkout, in
+# addition to versions of prebuilts. It should be bumped regularly by the
+# Fuchsia team – we aim for every 1-2 months.
+INTEGRATION_SHA=1011e3298775ee7cdf6f6dc73e808d6a86e33bd6
+
 checkout=fuchsia
 jiri=.jiri_root/bin/jiri
 
 set -x
 
-# This script will:
-# - create a directory named "fuchsia" if it does not exist
-# - download "jiri" to "fuchsia/.jiri_root/bin"
-curl -s "https://fuchsia.googlesource.com/jiri/+/HEAD/scripts/bootstrap_jiri?format=TEXT" \
-    | base64 --decode \
-    | bash -s $checkout
-
-cd $checkout
-
-$jiri init \
-    -partial=true \
-    -analytics-opt=false \
-    .
-
-$jiri import \
-    -name=integration \
-    -revision=$INTEGRATION_SHA \
-    -overwrite=true \
-    flower \
-    "https://fuchsia.googlesource.com/integration"
-
-if [ -d ".git" ]; then
-    # Wipe out any local changes if we're reusing a checkout.
-    git checkout --force JIRI_HEAD
-fi
+if [ -z "$KEEP_CHECKOUT" ]; then
+    # This script will:
+    # - create a directory named "fuchsia" if it does not exist
+    # - download "jiri" to "fuchsia/.jiri_root/bin"
+    curl -s "https://fuchsia.googlesource.com/jiri/+/HEAD/scripts/bootstrap_jiri?format=TEXT" \
+        | base64 --decode \
+        | bash -s $checkout
 
-$jiri update -autoupdate=false
+    cd $checkout
 
-echo integration commit = $(git -C integration rev-parse HEAD)
+    $jiri init \
+        -partial=true \
+        -analytics-opt=false \
+        .
 
-for git_ref in "${PICK_REFS[@]}"; do
-    git fetch https://fuchsia.googlesource.com/fuchsia $git_ref
-    git cherry-pick --no-commit FETCH_HEAD
-done
+    $jiri import \
+        -name=integration \
+        -revision=$INTEGRATION_SHA \
+        -overwrite=true \
+        flower \
+        "https://fuchsia.googlesource.com/integration"
+
+    if [ -d ".git" ]; then
+        # Wipe out any local changes if we're reusing a checkout.
+        git checkout --force JIRI_HEAD
+    fi
+
+    $jiri update -autoupdate=false
+
+    echo integration commit = $(git -C integration rev-parse HEAD)
+
+    for git_ref in "${PICK_REFS[@]}"; do
+        git fetch https://fuchsia.googlesource.com/fuchsia $git_ref
+        git cherry-pick --no-commit FETCH_HEAD
+    done
+else
+    echo Reusing existing Fuchsia checkout
+    cd $checkout
+fi
 
+# Run the script inside the Fuchsia checkout responsible for building Fuchsia.
+# You can change arguments to the build by setting KEEP_CHECKOUT=1 above and
+# modifying them in build_fuchsia_from_rust_ci.sh.
 bash scripts/rust/build_fuchsia_from_rust_ci.sh
diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md
index 3e1ac2fa8ad..72ab8ab5ce9 100644
--- a/src/doc/rustc/src/command-line-arguments.md
+++ b/src/doc/rustc/src/command-line-arguments.md
@@ -213,21 +213,39 @@ The valid emit kinds are:
   `CRATE_NAME.o`.
 
 The output filename can be set with the [`-o` flag](#option-o-output). A
-suffix may be added to the filename with the [`-C extra-filename`
-flag](codegen-options/index.md#extra-filename). The files are written to the
-current directory unless the [`--out-dir` flag](#option-out-dir) is used. Each
-emission type may also specify the output filename with the form `KIND=PATH`,
-which takes precedence over the `-o` flag.
-Specifying `-o -` or `--emit KIND=-` asks rustc to emit to stdout.
-Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to
-stdout despite it being a tty or not. This will result in an error if any
-binary output type is written to stdout that is a tty.
-This will also result in an error if multiple output types
-would be written to stdout, because they would be all mixed together.
+suffix may be added to the filename with the
+[`-C extra-filename` flag](codegen-options/index.md#extra-filename).
+
+Output files are written to the current directory unless the
+[`--out-dir` flag](#option-out-dir) is used.
 
 [LLVM bitcode]: https://llvm.org/docs/BitCodeFormat.html
 [LLVM IR]: https://llvm.org/docs/LangRef.html
 
+### Custom paths for individual emit kinds
+
+Each emit type can optionally be followed by `=` to specify an explicit output
+path that only applies to the output of that type. For example:
+
+- `--emit=link,dep-info=/path/to/dep-info.d`
+  - Emit the crate itself as normal,
+    and also emit dependency info to the specified path.
+- `--emit=llvm-ir=-,mir`
+  - Emit MIR to the default filename (based on crate name),
+    and emit LLVM IR to stdout.
+
+### Emitting to stdout
+
+When using `--emit` or [`-o`](#option-o-output), output can be sent to stdout
+by specifying `-` as the path (e.g. `-o -`).
+
+Binary output types can only be written to stdout if it is not a tty.
+Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to
+stdout regardless of whether it is a tty or not.
+
+Only one type of output can be written to stdout. Attempting to write multiple
+types to stdout at the same time will result in an error.
+
 <a id="option-print"></a>
 ## `--print`: print compiler information
 
diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs
index 20bcf1abf41..2857f74c744 100644
--- a/src/librustdoc/clean/cfg/tests.rs
+++ b/src/librustdoc/clean/cfg/tests.rs
@@ -1,6 +1,6 @@
 use super::*;
 
-use rustc_ast::{MetaItemLit, Path, StrStyle};
+use rustc_ast::{MetaItemLit, Path, Safety, StrStyle};
 use rustc_span::create_default_session_globals_then;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::DUMMY_SP;
@@ -16,6 +16,7 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg {
 
 fn dummy_meta_item_word(name: &str) -> MetaItem {
     MetaItem {
+        unsafety: Safety::Default,
         path: Path::from_ident(Ident::from_str(name)),
         kind: MetaItemKind::Word,
         span: DUMMY_SP,
@@ -25,6 +26,7 @@ fn dummy_meta_item_word(name: &str) -> MetaItem {
 fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem {
     let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP };
     MetaItem {
+        unsafety: Safety::Default,
         path: Path::from_ident(Ident::from_str(name)),
         kind: MetaItemKind::NameValue(lit),
         span: DUMMY_SP,
@@ -34,6 +36,7 @@ fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> Meta
 macro_rules! dummy_meta_item_list {
     ($name:ident, [$($list:ident),* $(,)?]) => {
         MetaItem {
+            unsafety: Safety::Default,
             path: Path::from_ident(Ident::from_str(stringify!($name))),
             kind: MetaItemKind::List(thin_vec![
                 $(
@@ -48,6 +51,7 @@ macro_rules! dummy_meta_item_list {
 
     ($name:ident, [$($list:expr),* $(,)?]) => {
         MetaItem {
+            unsafety: Safety::Default,
             path: Path::from_ident(Ident::from_str(stringify!($name))),
             kind: MetaItemKind::List(thin_vec![
                 $(
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 0d4bad6921d..a732e645b6b 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1,27 +1,23 @@
+mod make;
+mod markdown;
+mod rust;
+
+pub(crate) use make::make_test;
+pub(crate) use markdown::test as test_markdown;
+
 use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::sync::Lrc;
-use rustc_errors::emitter::stderr_destination;
 use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError};
-use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID};
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_hir::CRATE_HIR_ID;
 use rustc_interface::interface;
-use rustc_middle::hir::map::Map;
-use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::TyCtxt;
-use rustc_parse::new_parser_from_source_str;
-use rustc_parse::parser::attr::InnerAttrPolicy;
-use rustc_resolve::rustdoc::span_of_fragments;
 use rustc_session::config::{self, CrateType, ErrorOutputType};
-use rustc_session::parse::ParseSess;
-use rustc_session::{lint, Session};
+use rustc_session::lint;
 use rustc_span::edition::Edition;
-use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::sym;
-use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
+use rustc_span::FileName;
 use rustc_target::spec::{Target, TargetTriple};
 
-use std::env;
 use std::fs::File;
 use std::io::{self, Write};
 use std::panic;
@@ -33,14 +29,17 @@ use std::sync::{Arc, Mutex};
 
 use tempfile::{Builder as TempFileBuilder, TempDir};
 
-use crate::clean::{types::AttributesExt, Attributes};
 use crate::config::Options as RustdocOptions;
-use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
+use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine};
 use crate::lint::init_lints;
 
+use self::rust::HirCollector;
+
 /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
-#[derive(Clone, Default)]
+#[derive(Clone)]
 pub(crate) struct GlobalTestOptions {
+    /// Name of the crate (for regular `rustdoc`) or Markdown file (for `rustdoc foo.md`).
+    pub(crate) crate_name: String,
     /// Whether to disable the default `extern crate my_crate;` when creating doctests.
     pub(crate) no_crate_inject: bool,
     /// Whether inserting extra indent spaces in code block,
@@ -48,6 +47,8 @@ pub(crate) struct GlobalTestOptions {
     pub(crate) insert_indent_space: bool,
     /// Additional crate-level attributes to add to doctests.
     pub(crate) attrs: Vec<String>,
+    /// Path to file containing arguments for the invocation of rustc.
+    pub(crate) args_file: PathBuf,
 }
 
 pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> {
@@ -80,7 +81,7 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) ->
 
     let content = content.join("\n");
 
-    file.write(content.as_bytes())
+    file.write_all(content.as_bytes())
         .map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?;
     Ok(())
 }
@@ -166,43 +167,28 @@ pub(crate) fn run(
         Ok(temp_dir) => temp_dir,
         Err(error) => return crate::wrap_return(dcx, Err(error)),
     };
-    let file_path = temp_dir.path().join("rustdoc-cfgs");
-    crate::wrap_return(dcx, generate_args_file(&file_path, &options))?;
+    let args_path = temp_dir.path().join("rustdoc-cfgs");
+    crate::wrap_return(dcx, generate_args_file(&args_path, &options))?;
 
     let (tests, unused_extern_reports, compiling_test_count) =
         interface::run_compiler(config, |compiler| {
             compiler.enter(|queries| {
                 let collector = queries.global_ctxt()?.enter(|tcx| {
+                    let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
                     let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID);
-
-                    let opts = scrape_test_config(crate_attrs);
+                    let opts = scrape_test_config(crate_name, crate_attrs, args_path);
                     let enable_per_target_ignores = options.enable_per_target_ignores;
-                    let mut collector = Collector::new(
-                        tcx.crate_name(LOCAL_CRATE).to_string(),
-                        options,
-                        false,
-                        opts,
-                        Some(compiler.sess.psess.clone_source_map()),
-                        None,
-                        enable_per_target_ignores,
-                        file_path,
-                    );
 
-                    let mut hir_collector = HirCollector {
-                        sess: &compiler.sess,
-                        collector: &mut collector,
-                        map: tcx.hir(),
-                        codes: ErrorCodes::from(
-                            compiler.sess.opts.unstable_features.is_nightly_build(),
-                        ),
+                    let mut collector = CreateRunnableDoctests::new(options, opts);
+                    let hir_collector = HirCollector::new(
+                        &compiler.sess,
+                        tcx.hir(),
+                        ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()),
+                        enable_per_target_ignores,
                         tcx,
-                    };
-                    hir_collector.visit_testable(
-                        "".to_string(),
-                        CRATE_DEF_ID,
-                        tcx.hir().span(CRATE_HIR_ID),
-                        |this| tcx.hir().walk_toplevel_module(this),
                     );
+                    let tests = hir_collector.collect_crate();
+                    tests.into_iter().for_each(|t| collector.add_test(t));
 
                     collector
                 });
@@ -273,11 +259,20 @@ pub(crate) fn run_tests(
 }
 
 // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade.
-fn scrape_test_config(attrs: &[ast::Attribute]) -> GlobalTestOptions {
+fn scrape_test_config(
+    crate_name: String,
+    attrs: &[ast::Attribute],
+    args_file: PathBuf,
+) -> GlobalTestOptions {
     use rustc_ast_pretty::pprust;
 
-    let mut opts =
-        GlobalTestOptions { no_crate_inject: false, attrs: Vec::new(), insert_indent_space: false };
+    let mut opts = GlobalTestOptions {
+        crate_name,
+        no_crate_inject: false,
+        attrs: Vec::new(),
+        insert_indent_space: false,
+        args_file,
+    };
 
     let test_attrs: Vec<_> = attrs
         .iter()
@@ -370,30 +365,25 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com
     command
 }
 
+struct RunnableDoctest {
+    full_test_code: String,
+    full_test_line_offset: usize,
+    test_opts: IndividualTestOptions,
+    global_opts: GlobalTestOptions,
+    scraped_test: ScrapedDoctest,
+}
+
 fn run_test(
-    test: &str,
-    crate_name: &str,
-    line: usize,
-    rustdoc_options: IndividualTestOptions,
-    mut lang_string: LangString,
-    no_run: bool,
-    opts: &GlobalTestOptions,
-    edition: Edition,
-    path: PathBuf,
+    doctest: RunnableDoctest,
+    rustdoc_options: &RustdocOptions,
+    supports_color: bool,
     report_unused_externs: impl Fn(UnusedExterns),
 ) -> Result<(), TestFailure> {
-    let (test, line_offset, supports_color) = make_test(
-        test,
-        Some(crate_name),
-        lang_string.test_harness,
-        opts,
-        edition,
-        Some(&rustdoc_options.test_id),
-    );
-
+    let scraped_test = &doctest.scraped_test;
+    let langstr = &scraped_test.langstr;
     // Make sure we emit well-formed executable names for our target.
     let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target);
-    let output_file = rustdoc_options.outdir.path().join(rust_out);
+    let output_file = doctest.test_opts.outdir.path().join(rust_out);
 
     let rustc_binary = rustdoc_options
         .test_builder
@@ -401,33 +391,41 @@ fn run_test(
         .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc"));
     let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
 
-    compiler.arg(&format!("@{}", rustdoc_options.arg_file.display()));
+    compiler.arg(&format!("@{}", doctest.global_opts.args_file.display()));
 
     if let Some(sysroot) = &rustdoc_options.maybe_sysroot {
         compiler.arg(format!("--sysroot={}", sysroot.display()));
     }
 
-    compiler.arg("--edition").arg(&edition.to_string());
-    compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path);
-    compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize));
+    compiler.arg("--edition").arg(&scraped_test.edition(rustdoc_options).to_string());
+    compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path);
+    compiler.env(
+        "UNSTABLE_RUSTDOC_TEST_LINE",
+        format!("{}", scraped_test.line as isize - doctest.full_test_line_offset as isize),
+    );
     compiler.arg("-o").arg(&output_file);
-    if lang_string.test_harness {
+    if langstr.test_harness {
         compiler.arg("--test");
     }
-    if rustdoc_options.is_json_unused_externs_enabled && !lang_string.compile_fail {
+    if rustdoc_options.json_unused_externs.is_enabled() && !langstr.compile_fail {
         compiler.arg("--error-format=json");
         compiler.arg("--json").arg("unused-externs");
         compiler.arg("-W").arg("unused_crate_dependencies");
         compiler.arg("-Z").arg("unstable-options");
     }
 
-    if no_run && !lang_string.compile_fail && rustdoc_options.should_persist_doctests {
+    if scraped_test.no_run(rustdoc_options)
+        && !langstr.compile_fail
+        && rustdoc_options.persist_doctests.is_none()
+    {
+        // FIXME: why does this code check if it *shouldn't* persist doctests
+        //        -- shouldn't it be the negation?
         compiler.arg("--emit=metadata");
     }
-    compiler.arg("--target").arg(match rustdoc_options.target {
+    compiler.arg("--target").arg(match &rustdoc_options.target {
         TargetTriple::TargetTriple(s) => s,
         TargetTriple::TargetJson { path_for_rustdoc, .. } => {
-            path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string()
+            path_for_rustdoc.to_str().expect("target path must be valid unicode")
         }
     });
     if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format {
@@ -459,7 +457,7 @@ fn run_test(
     let mut child = compiler.spawn().expect("Failed to spawn rustc process");
     {
         let stdin = child.stdin.as_mut().expect("Failed to open stdin");
-        stdin.write_all(test.as_bytes()).expect("could write out test sources");
+        stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources");
     }
     let output = child.wait_with_output().expect("Failed to read stdout");
 
@@ -490,20 +488,26 @@ fn run_test(
     }
 
     let _bomb = Bomb(&out);
-    match (output.status.success(), lang_string.compile_fail) {
+    match (output.status.success(), langstr.compile_fail) {
         (true, true) => {
             return Err(TestFailure::UnexpectedCompilePass);
         }
         (true, false) => {}
         (false, true) => {
-            if !lang_string.error_codes.is_empty() {
+            if !langstr.error_codes.is_empty() {
                 // We used to check if the output contained "error[{}]: " but since we added the
                 // colored output, we can't anymore because of the color escape characters before
                 // the ":".
-                lang_string.error_codes.retain(|err| !out.contains(&format!("error[{err}]")));
-
-                if !lang_string.error_codes.is_empty() {
-                    return Err(TestFailure::MissingErrorCodes(lang_string.error_codes));
+                let missing_codes: Vec<String> = scraped_test
+                    .langstr
+                    .error_codes
+                    .iter()
+                    .filter(|err| !out.contains(&format!("error[{err}]")))
+                    .cloned()
+                    .collect();
+
+                if !missing_codes.is_empty() {
+                    return Err(TestFailure::MissingErrorCodes(missing_codes));
                 }
             }
         }
@@ -512,7 +516,7 @@ fn run_test(
         }
     }
 
-    if no_run {
+    if scraped_test.no_run(rustdoc_options) {
         return Ok(());
     }
 
@@ -520,15 +524,15 @@ fn run_test(
     let mut cmd;
 
     let output_file = make_maybe_absolute_path(output_file);
-    if let Some(tool) = rustdoc_options.runtool {
+    if let Some(tool) = &rustdoc_options.runtool {
         let tool = make_maybe_absolute_path(tool.into());
         cmd = Command::new(tool);
-        cmd.args(rustdoc_options.runtool_args);
+        cmd.args(&rustdoc_options.runtool_args);
         cmd.arg(output_file);
     } else {
         cmd = Command::new(output_file);
     }
-    if let Some(run_directory) = rustdoc_options.test_run_directory {
+    if let Some(run_directory) = &rustdoc_options.test_run_directory {
         cmd.current_dir(run_directory);
     }
 
@@ -544,9 +548,9 @@ fn run_test(
     match result {
         Err(e) => return Err(TestFailure::ExecutionError(e)),
         Ok(out) => {
-            if lang_string.should_panic && out.status.success() {
+            if langstr.should_panic && out.status.success() {
                 return Err(TestFailure::UnexpectedRunPass);
-            } else if !lang_string.should_panic && !out.status.success() {
+            } else if !langstr.should_panic && !out.status.success() {
                 return Err(TestFailure::ExecutionFailure(out));
             }
         }
@@ -569,387 +573,14 @@ fn make_maybe_absolute_path(path: PathBuf) -> PathBuf {
     }
 }
 
-/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
-/// lines before the test code begins as well as if the output stream supports colors or not.
-pub(crate) fn make_test(
-    s: &str,
-    crate_name: Option<&str>,
-    dont_insert_main: bool,
-    opts: &GlobalTestOptions,
-    edition: Edition,
-    test_id: Option<&str>,
-) -> (String, usize, bool) {
-    let (crate_attrs, everything_else, crates) = partition_source(s, edition);
-    let everything_else = everything_else.trim();
-    let mut line_offset = 0;
-    let mut prog = String::new();
-    let mut supports_color = false;
-
-    if opts.attrs.is_empty() {
-        // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
-        // lints that are commonly triggered in doctests. The crate-level test attributes are
-        // commonly used to make tests fail in case they trigger warnings, so having this there in
-        // that case may cause some tests to pass when they shouldn't have.
-        prog.push_str("#![allow(unused)]\n");
-        line_offset += 1;
-    }
-
-    // Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
-    for attr in &opts.attrs {
-        prog.push_str(&format!("#![{attr}]\n"));
-        line_offset += 1;
-    }
-
-    // Now push any outer attributes from the example, assuming they
-    // are intended to be crate attributes.
-    prog.push_str(&crate_attrs);
-    prog.push_str(&crates);
-
-    // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
-    // crate already is included.
-    let result = rustc_driver::catch_fatal_errors(|| {
-        rustc_span::create_session_if_not_set_then(edition, |_| {
-            use rustc_errors::emitter::{Emitter, HumanEmitter};
-            use rustc_errors::DiagCtxt;
-            use rustc_parse::parser::ForceCollect;
-            use rustc_span::source_map::FilePathMapping;
-
-            let filename = FileName::anon_source_code(s);
-            let source = crates + everything_else;
-
-            // Any errors in parsing should also appear when the doctest is compiled for real, so just
-            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
-            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-            let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-                rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-                false,
-            );
-            supports_color =
-                HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
-                    .supports_color();
-
-            let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
-
-            // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
-            let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
-            let psess = ParseSess::with_dcx(dcx, sm);
-
-            let mut found_main = false;
-            let mut found_extern_crate = crate_name.is_none();
-            let mut found_macro = false;
-
-            let mut parser = match new_parser_from_source_str(&psess, filename, source) {
-                Ok(p) => p,
-                Err(errs) => {
-                    errs.into_iter().for_each(|err| err.cancel());
-                    return (found_main, found_extern_crate, found_macro);
-                }
-            };
-
-            loop {
-                match parser.parse_item(ForceCollect::No) {
-                    Ok(Some(item)) => {
-                        if !found_main
-                            && let ast::ItemKind::Fn(..) = item.kind
-                            && item.ident.name == sym::main
-                        {
-                            found_main = true;
-                        }
-
-                        if !found_extern_crate
-                            && let ast::ItemKind::ExternCrate(original) = item.kind
-                        {
-                            // This code will never be reached if `crate_name` is none because
-                            // `found_extern_crate` is initialized to `true` if it is none.
-                            let crate_name = crate_name.unwrap();
-
-                            match original {
-                                Some(name) => found_extern_crate = name.as_str() == crate_name,
-                                None => found_extern_crate = item.ident.as_str() == crate_name,
-                            }
-                        }
-
-                        if !found_macro && let ast::ItemKind::MacCall(..) = item.kind {
-                            found_macro = true;
-                        }
-
-                        if found_main && found_extern_crate {
-                            break;
-                        }
-                    }
-                    Ok(None) => break,
-                    Err(e) => {
-                        e.cancel();
-                        break;
-                    }
-                }
-
-                // The supplied item is only used for diagnostics,
-                // which are swallowed here anyway.
-                parser.maybe_consume_incorrect_semicolon(None);
-            }
-
-            // Reset errors so that they won't be reported as compiler bugs when dropping the
-            // dcx. Any errors in the tests will be reported when the test file is compiled,
-            // Note that we still need to cancel the errors above otherwise `Diag` will panic on
-            // drop.
-            psess.dcx.reset_err_count();
-
-            (found_main, found_extern_crate, found_macro)
-        })
-    });
-    let Ok((already_has_main, already_has_extern_crate, found_macro)) = result else {
-        // If the parser panicked due to a fatal error, pass the test code through unchanged.
-        // The error will be reported during compilation.
-        return (s.to_owned(), 0, false);
-    };
-
-    // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
-    // see it. In that case, run the old text-based scan to see if they at least have a main
-    // function written inside a macro invocation. See
-    // https://github.com/rust-lang/rust/issues/56898
-    let already_has_main = if found_macro && !already_has_main {
-        s.lines()
-            .map(|line| {
-                let comment = line.find("//");
-                if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line }
-            })
-            .any(|code| code.contains("fn main"))
-    } else {
-        already_has_main
-    };
-
-    // Don't inject `extern crate std` because it's already injected by the
-    // compiler.
-    if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") {
-        if let Some(crate_name) = crate_name {
-            // Don't inject `extern crate` if the crate is never used.
-            // NOTE: this is terribly inaccurate because it doesn't actually
-            // parse the source, but only has false positives, not false
-            // negatives.
-            if s.contains(crate_name) {
-                // rustdoc implicitly inserts an `extern crate` item for the own crate
-                // which may be unused, so we need to allow the lint.
-                prog.push_str("#[allow(unused_extern_crates)]\n");
-
-                prog.push_str(&format!("extern crate r#{crate_name};\n"));
-                line_offset += 1;
-            }
-        }
-    }
-
-    // FIXME: This code cannot yet handle no_std test cases yet
-    if dont_insert_main || already_has_main || prog.contains("![no_std]") {
-        prog.push_str(everything_else);
-    } else {
-        let returns_result = everything_else.trim_end().ends_with("(())");
-        // Give each doctest main function a unique name.
-        // This is for example needed for the tooling around `-C instrument-coverage`.
-        let inner_fn_name = if let Some(test_id) = test_id {
-            format!("_doctest_main_{test_id}")
-        } else {
-            "_inner".into()
-        };
-        let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" };
-        let (main_pre, main_post) = if returns_result {
-            (
-                format!(
-                    "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n",
-                ),
-                format!("\n}} {inner_fn_name}().unwrap() }}"),
-            )
-        } else if test_id.is_some() {
-            (
-                format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",),
-                format!("\n}} {inner_fn_name}() }}"),
-            )
-        } else {
-            ("fn main() {\n".into(), "\n}".into())
-        };
-        // Note on newlines: We insert a line/newline *before*, and *after*
-        // the doctest and adjust the `line_offset` accordingly.
-        // In the case of `-C instrument-coverage`, this means that the generated
-        // inner `main` function spans from the doctest opening codeblock to the
-        // closing one. For example
-        // /// ``` <- start of the inner main
-        // /// <- code under doctest
-        // /// ``` <- end of the inner main
-        line_offset += 1;
-
-        // add extra 4 spaces for each line to offset the code block
-        let content = if opts.insert_indent_space {
-            everything_else
-                .lines()
-                .map(|line| format!("    {}", line))
-                .collect::<Vec<String>>()
-                .join("\n")
-        } else {
-            everything_else.to_string()
-        };
-        prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned());
-    }
-
-    debug!("final doctest:\n{prog}");
-
-    (prog, line_offset, supports_color)
-}
-
-fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
-    if source.is_empty() {
-        // Empty content so nothing to check in here...
-        return true;
-    }
-    rustc_driver::catch_fatal_errors(|| {
-        rustc_span::create_session_if_not_set_then(edition, |_| {
-            use rustc_errors::emitter::HumanEmitter;
-            use rustc_errors::DiagCtxt;
-            use rustc_span::source_map::FilePathMapping;
-
-            let filename = FileName::anon_source_code(source);
-            // Any errors in parsing should also appear when the doctest is compiled for real, so just
-            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
-            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-            let fallback_bundle = rustc_errors::fallback_fluent_bundle(
-                rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
-                false,
-            );
-
-            let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
-
-            let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
-            let psess = ParseSess::with_dcx(dcx, sm);
-            let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) {
-                Ok(p) => p,
-                Err(errs) => {
-                    errs.into_iter().for_each(|err| err.cancel());
-                    // If there is an unclosed delimiter, an error will be returned by the
-                    // tokentrees.
-                    return false;
-                }
-            };
-            // If a parsing error happened, it's very likely that the attribute is incomplete.
-            if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) {
-                e.cancel();
-                return false;
-            }
-            true
-        })
-    })
-    .unwrap_or(false)
-}
-
-fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
-    #[derive(Copy, Clone, PartialEq)]
-    enum PartitionState {
-        Attrs,
-        Crates,
-        Other,
-    }
-    let mut state = PartitionState::Attrs;
-    let mut before = String::new();
-    let mut crates = String::new();
-    let mut after = String::new();
-
-    let mut mod_attr_pending = String::new();
-
-    for line in s.lines() {
-        let trimline = line.trim();
-
-        // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be
-        // shunted into "everything else"
-        match state {
-            PartitionState::Attrs => {
-                state = if trimline.starts_with("#![") {
-                    if !check_if_attr_is_complete(line, edition) {
-                        mod_attr_pending = line.to_owned();
-                    } else {
-                        mod_attr_pending.clear();
-                    }
-                    PartitionState::Attrs
-                } else if trimline.chars().all(|c| c.is_whitespace())
-                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
-                {
-                    PartitionState::Attrs
-                } else if trimline.starts_with("extern crate")
-                    || trimline.starts_with("#[macro_use] extern crate")
-                {
-                    PartitionState::Crates
-                } else {
-                    // First we check if the previous attribute was "complete"...
-                    if !mod_attr_pending.is_empty() {
-                        // If not, then we append the new line into the pending attribute to check
-                        // if this time it's complete...
-                        mod_attr_pending.push_str(line);
-                        if !trimline.is_empty()
-                            && check_if_attr_is_complete(&mod_attr_pending, edition)
-                        {
-                            // If it's complete, then we can clear the pending content.
-                            mod_attr_pending.clear();
-                        }
-                        // In any case, this is considered as `PartitionState::Attrs` so it's
-                        // prepended before rustdoc's inserts.
-                        PartitionState::Attrs
-                    } else {
-                        PartitionState::Other
-                    }
-                };
-            }
-            PartitionState::Crates => {
-                state = if trimline.starts_with("extern crate")
-                    || trimline.starts_with("#[macro_use] extern crate")
-                    || trimline.chars().all(|c| c.is_whitespace())
-                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
-                {
-                    PartitionState::Crates
-                } else {
-                    PartitionState::Other
-                };
-            }
-            PartitionState::Other => {}
-        }
-
-        match state {
-            PartitionState::Attrs => {
-                before.push_str(line);
-                before.push('\n');
-            }
-            PartitionState::Crates => {
-                crates.push_str(line);
-                crates.push('\n');
-            }
-            PartitionState::Other => {
-                after.push_str(line);
-                after.push('\n');
-            }
-        }
-    }
-
-    debug!("before:\n{before}");
-    debug!("crates:\n{crates}");
-    debug!("after:\n{after}");
-
-    (before, after, crates)
-}
-
-pub(crate) struct IndividualTestOptions {
-    test_builder: Option<PathBuf>,
-    test_builder_wrappers: Vec<PathBuf>,
-    is_json_unused_externs_enabled: bool,
-    should_persist_doctests: bool,
-    error_format: ErrorOutputType,
-    test_run_directory: Option<PathBuf>,
-    nocapture: bool,
-    arg_file: PathBuf,
+struct IndividualTestOptions {
     outdir: DirState,
-    runtool: Option<String>,
-    runtool_args: Vec<String>,
-    target: TargetTriple,
     test_id: String,
-    maybe_sysroot: Option<PathBuf>,
+    path: PathBuf,
 }
 
 impl IndividualTestOptions {
-    fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self {
+    fn new(options: &RustdocOptions, test_id: String, test_path: PathBuf) -> Self {
         let outdir = if let Some(ref path) = options.persist_doctests {
             let mut path = path.clone();
             path.push(&test_id);
@@ -964,103 +595,58 @@ impl IndividualTestOptions {
             DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir"))
         };
 
-        Self {
-            test_builder: options.test_builder.clone(),
-            test_builder_wrappers: options.test_builder_wrappers.clone(),
-            is_json_unused_externs_enabled: options.json_unused_externs.is_enabled(),
-            should_persist_doctests: options.persist_doctests.is_none(),
-            error_format: options.error_format,
-            test_run_directory: options.test_run_directory.clone(),
-            nocapture: options.nocapture,
-            arg_file: arg_file.into(),
-            outdir,
-            runtool: options.runtool.clone(),
-            runtool_args: options.runtool_args.clone(),
-            target: options.target.clone(),
-            test_id,
-            maybe_sysroot: options.maybe_sysroot.clone(),
-        }
+        Self { outdir, test_id, path: test_path }
     }
 }
 
-pub(crate) trait Tester {
-    fn add_test(&mut self, test: String, config: LangString, line: usize);
-    fn get_line(&self) -> usize {
-        0
+/// A doctest scraped from the code, ready to be turned into a runnable test.
+struct ScrapedDoctest {
+    filename: FileName,
+    line: usize,
+    logical_path: Vec<String>,
+    langstr: LangString,
+    text: String,
+}
+
+impl ScrapedDoctest {
+    fn edition(&self, opts: &RustdocOptions) -> Edition {
+        self.langstr.edition.unwrap_or(opts.edition)
+    }
+
+    fn no_run(&self, opts: &RustdocOptions) -> bool {
+        self.langstr.no_run || opts.no_run
     }
-    fn register_header(&mut self, _name: &str, _level: u32) {}
 }
 
-pub(crate) struct Collector {
-    pub(crate) tests: Vec<test::TestDescAndFn>,
-
-    // The name of the test displayed to the user, separated by `::`.
-    //
-    // In tests from Rust source, this is the path to the item
-    // e.g., `["std", "vec", "Vec", "push"]`.
-    //
-    // In tests from a markdown file, this is the titles of all headers (h1~h6)
-    // of the sections that contain the code block, e.g., if the markdown file is
-    // written as:
-    //
-    // ``````markdown
-    // # Title
-    //
-    // ## Subtitle
-    //
-    // ```rust
-    // assert!(true);
-    // ```
-    // ``````
-    //
-    // the `names` vector of that test will be `["Title", "Subtitle"]`.
-    names: Vec<String>,
-
-    rustdoc_options: RustdocOptions,
-    use_headers: bool,
-    enable_per_target_ignores: bool,
-    crate_name: String,
+pub(crate) trait DoctestVisitor {
+    fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine);
+    fn visit_header(&mut self, _name: &str, _level: u32) {}
+}
+
+struct CreateRunnableDoctests {
+    tests: Vec<test::TestDescAndFn>,
+
+    rustdoc_options: Arc<RustdocOptions>,
     opts: GlobalTestOptions,
-    position: Span,
-    source_map: Option<Lrc<SourceMap>>,
-    filename: Option<PathBuf>,
     visited_tests: FxHashMap<(String, usize), usize>,
     unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>,
     compiling_test_count: AtomicUsize,
-    arg_file: PathBuf,
 }
 
-impl Collector {
-    pub(crate) fn new(
-        crate_name: String,
-        rustdoc_options: RustdocOptions,
-        use_headers: bool,
-        opts: GlobalTestOptions,
-        source_map: Option<Lrc<SourceMap>>,
-        filename: Option<PathBuf>,
-        enable_per_target_ignores: bool,
-        arg_file: PathBuf,
-    ) -> Collector {
-        Collector {
+impl CreateRunnableDoctests {
+    fn new(rustdoc_options: RustdocOptions, opts: GlobalTestOptions) -> CreateRunnableDoctests {
+        CreateRunnableDoctests {
             tests: Vec::new(),
-            names: Vec::new(),
-            rustdoc_options,
-            use_headers,
-            enable_per_target_ignores,
-            crate_name,
+            rustdoc_options: Arc::new(rustdoc_options),
             opts,
-            position: DUMMY_SP,
-            source_map,
-            filename,
             visited_tests: FxHashMap::default(),
             unused_extern_reports: Default::default(),
             compiling_test_count: AtomicUsize::new(0),
-            arg_file,
         }
     }
 
-    fn generate_name(&self, line: usize, filename: &FileName) -> String {
-        let mut item_path = self.names.join("::");
+    fn generate_name(&self, filename: &FileName, line: usize, logical_path: &[String]) -> String {
+        let mut item_path = logical_path.join("::");
         item_path.retain(|c| c != ' ');
         if !item_path.is_empty() {
             item_path.push(' ');
@@ -1068,44 +654,16 @@ impl Collector {
         format!("{} - {item_path}(line {line})", filename.prefer_local())
     }
 
-    pub(crate) fn set_position(&mut self, position: Span) {
-        self.position = position;
-    }
-
-    fn get_filename(&self) -> FileName {
-        if let Some(ref source_map) = self.source_map {
-            let filename = source_map.span_to_filename(self.position);
-            if let FileName::Real(ref filename) = filename
-                && let Ok(cur_dir) = env::current_dir()
-                && let Some(local_path) = filename.local_path()
-                && let Ok(path) = local_path.strip_prefix(&cur_dir)
-            {
-                return path.to_owned().into();
-            }
-            filename
-        } else if let Some(ref filename) = self.filename {
-            filename.clone().into()
-        } else {
-            FileName::Custom("input".to_owned())
-        }
-    }
-}
-
-impl Tester for Collector {
-    fn add_test(&mut self, test: String, config: LangString, line: usize) {
-        let filename = self.get_filename();
-        let name = self.generate_name(line, &filename);
-        let crate_name = self.crate_name.clone();
+    fn add_test(&mut self, test: ScrapedDoctest) {
+        let name = self.generate_name(&test.filename, test.line, &test.logical_path);
         let opts = self.opts.clone();
-        let edition = config.edition.unwrap_or(self.rustdoc_options.edition);
         let target_str = self.rustdoc_options.target.to_string();
         let unused_externs = self.unused_extern_reports.clone();
-        let no_run = config.no_run || self.rustdoc_options.no_run;
-        if !config.compile_fail {
+        if !test.langstr.compile_fail {
             self.compiling_test_count.fetch_add(1, Ordering::SeqCst);
         }
 
-        let path = match &filename {
+        let path = match &test.filename {
             FileName::Real(path) => {
                 if let Some(local_path) = path.local_path() {
                     local_path.to_path_buf()
@@ -1118,7 +676,8 @@ impl Tester for Collector {
         };
 
         // For example `module/file.rs` would become `module_file_rs`
-        let file = filename
+        let file = test
+            .filename
             .prefer_local()
             .to_string_lossy()
             .chars()
@@ -1127,22 +686,25 @@ impl Tester for Collector {
         let test_id = format!(
             "{file}_{line}_{number}",
             file = file,
-            line = line,
+            line = test.line,
             number = {
                 // Increases the current test number, if this file already
                 // exists or it creates a new entry with a test number of 0.
-                self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0)
+                self.visited_tests
+                    .entry((file.clone(), test.line))
+                    .and_modify(|v| *v += 1)
+                    .or_insert(0)
             },
         );
 
-        let rustdoc_test_options =
-            IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id);
+        let rustdoc_options = self.rustdoc_options.clone();
+        let rustdoc_test_options = IndividualTestOptions::new(&self.rustdoc_options, test_id, path);
 
-        debug!("creating test {name}: {test}");
+        debug!("creating test {name}: {}", test.text);
         self.tests.push(test::TestDescAndFn {
             desc: test::TestDesc {
                 name: test::DynTestName(name),
-                ignore: match config.ignore {
+                ignore: match test.langstr.ignore {
                     Ignore::All => true,
                     Ignore::None => false,
                     Ignore::Some(ref ignores) => ignores.iter().any(|s| target_str.contains(s)),
@@ -1155,252 +717,103 @@ impl Tester for Collector {
                 end_col: 0,
                 // compiler failures are test failures
                 should_panic: test::ShouldPanic::No,
-                compile_fail: config.compile_fail,
-                no_run,
+                compile_fail: test.langstr.compile_fail,
+                no_run: test.no_run(&rustdoc_options),
                 test_type: test::TestType::DocTest,
             },
             testfn: test::DynTestFn(Box::new(move || {
-                let report_unused_externs = |uext| {
-                    unused_externs.lock().unwrap().push(uext);
-                };
-                let res = run_test(
-                    &test,
-                    &crate_name,
-                    line,
-                    rustdoc_test_options,
-                    config,
-                    no_run,
-                    &opts,
-                    edition,
-                    path,
-                    report_unused_externs,
-                );
-
-                if let Err(err) = res {
-                    match err {
-                        TestFailure::CompileError => {
-                            eprint!("Couldn't compile the test.");
-                        }
-                        TestFailure::UnexpectedCompilePass => {
-                            eprint!("Test compiled successfully, but it's marked `compile_fail`.");
-                        }
-                        TestFailure::UnexpectedRunPass => {
-                            eprint!("Test executable succeeded, but it's marked `should_panic`.");
-                        }
-                        TestFailure::MissingErrorCodes(codes) => {
-                            eprint!("Some expected error codes were not found: {codes:?}");
-                        }
-                        TestFailure::ExecutionError(err) => {
-                            eprint!("Couldn't run the test: {err}");
-                            if err.kind() == io::ErrorKind::PermissionDenied {
-                                eprint!(" - maybe your tempdir is mounted with noexec?");
-                            }
-                        }
-                        TestFailure::ExecutionFailure(out) => {
-                            eprintln!("Test executable failed ({reason}).", reason = out.status);
-
-                            // FIXME(#12309): An unfortunate side-effect of capturing the test
-                            // executable's output is that the relative ordering between the test's
-                            // stdout and stderr is lost. However, this is better than the
-                            // alternative: if the test executable inherited the parent's I/O
-                            // handles the output wouldn't be captured at all, even on success.
-                            //
-                            // The ordering could be preserved if the test process' stderr was
-                            // redirected to stdout, but that functionality does not exist in the
-                            // standard library, so it may not be portable enough.
-                            let stdout = str::from_utf8(&out.stdout).unwrap_or_default();
-                            let stderr = str::from_utf8(&out.stderr).unwrap_or_default();
-
-                            if !stdout.is_empty() || !stderr.is_empty() {
-                                eprintln!();
-
-                                if !stdout.is_empty() {
-                                    eprintln!("stdout:\n{stdout}");
-                                }
-
-                                if !stderr.is_empty() {
-                                    eprintln!("stderr:\n{stderr}");
-                                }
-                            }
-                        }
-                    }
-
-                    panic::resume_unwind(Box::new(()));
-                }
-                Ok(())
+                doctest_run_fn(rustdoc_test_options, opts, test, rustdoc_options, unused_externs)
             })),
         });
     }
+}
 
-    fn get_line(&self) -> usize {
-        if let Some(ref source_map) = self.source_map {
-            let line = self.position.lo().to_usize();
-            let line = source_map.lookup_char_pos(BytePos(line as u32)).line;
-            if line > 0 { line - 1 } else { line }
-        } else {
-            0
-        }
-    }
+fn doctest_run_fn(
+    test_opts: IndividualTestOptions,
+    global_opts: GlobalTestOptions,
+    scraped_test: ScrapedDoctest,
+    rustdoc_options: Arc<RustdocOptions>,
+    unused_externs: Arc<Mutex<Vec<UnusedExterns>>>,
+) -> Result<(), String> {
+    let report_unused_externs = |uext| {
+        unused_externs.lock().unwrap().push(uext);
+    };
+    let edition = scraped_test.edition(&rustdoc_options);
+    let (full_test_code, full_test_line_offset, supports_color) = make_test(
+        &scraped_test.text,
+        Some(&global_opts.crate_name),
+        scraped_test.langstr.test_harness,
+        &global_opts,
+        edition,
+        Some(&test_opts.test_id),
+    );
+    let runnable_test = RunnableDoctest {
+        full_test_code,
+        full_test_line_offset,
+        test_opts,
+        global_opts,
+        scraped_test,
+    };
+    let res = run_test(runnable_test, &rustdoc_options, supports_color, report_unused_externs);
 
-    fn register_header(&mut self, name: &str, level: u32) {
-        if self.use_headers {
-            // We use these headings as test names, so it's good if
-            // they're valid identifiers.
-            let name = name
-                .chars()
-                .enumerate()
-                .map(|(i, c)| {
-                    if (i == 0 && rustc_lexer::is_id_start(c))
-                        || (i != 0 && rustc_lexer::is_id_continue(c))
-                    {
-                        c
-                    } else {
-                        '_'
-                    }
-                })
-                .collect::<String>();
-
-            // Here we try to efficiently assemble the header titles into the
-            // test name in the form of `h1::h2::h3::h4::h5::h6`.
-            //
-            // Suppose that originally `self.names` contains `[h1, h2, h3]`...
-            let level = level as usize;
-            if level <= self.names.len() {
-                // ... Consider `level == 2`. All headers in the lower levels
-                // are irrelevant in this new level. So we should reset
-                // `self.names` to contain headers until <h2>, and replace that
-                // slot with the new name: `[h1, name]`.
-                self.names.truncate(level);
-                self.names[level - 1] = name;
-            } else {
-                // ... On the other hand, consider `level == 5`. This means we
-                // need to extend `self.names` to contain five headers. We fill
-                // in the missing level (<h4>) with `_`. Thus `self.names` will
-                // become `[h1, h2, h3, "_", name]`.
-                if level - 1 > self.names.len() {
-                    self.names.resize(level - 1, "_".to_owned());
+    if let Err(err) = res {
+        match err {
+            TestFailure::CompileError => {
+                eprint!("Couldn't compile the test.");
+            }
+            TestFailure::UnexpectedCompilePass => {
+                eprint!("Test compiled successfully, but it's marked `compile_fail`.");
+            }
+            TestFailure::UnexpectedRunPass => {
+                eprint!("Test executable succeeded, but it's marked `should_panic`.");
+            }
+            TestFailure::MissingErrorCodes(codes) => {
+                eprint!("Some expected error codes were not found: {codes:?}");
+            }
+            TestFailure::ExecutionError(err) => {
+                eprint!("Couldn't run the test: {err}");
+                if err.kind() == io::ErrorKind::PermissionDenied {
+                    eprint!(" - maybe your tempdir is mounted with noexec?");
                 }
-                self.names.push(name);
             }
-        }
-    }
-}
-
-#[cfg(test)] // used in tests
-impl Tester for Vec<usize> {
-    fn add_test(&mut self, _test: String, _config: LangString, line: usize) {
-        self.push(line);
-    }
-}
-
-struct HirCollector<'a, 'hir, 'tcx> {
-    sess: &'a Session,
-    collector: &'a mut Collector,
-    map: Map<'hir>,
-    codes: ErrorCodes,
-    tcx: TyCtxt<'tcx>,
-}
+            TestFailure::ExecutionFailure(out) => {
+                eprintln!("Test executable failed ({reason}).", reason = out.status);
+
+                // FIXME(#12309): An unfortunate side-effect of capturing the test
+                // executable's output is that the relative ordering between the test's
+                // stdout and stderr is lost. However, this is better than the
+                // alternative: if the test executable inherited the parent's I/O
+                // handles the output wouldn't be captured at all, even on success.
+                //
+                // The ordering could be preserved if the test process' stderr was
+                // redirected to stdout, but that functionality does not exist in the
+                // standard library, so it may not be portable enough.
+                let stdout = str::from_utf8(&out.stdout).unwrap_or_default();
+                let stderr = str::from_utf8(&out.stderr).unwrap_or_default();
+
+                if !stdout.is_empty() || !stderr.is_empty() {
+                    eprintln!();
+
+                    if !stdout.is_empty() {
+                        eprintln!("stdout:\n{stdout}");
+                    }
 
-impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
-    fn visit_testable<F: FnOnce(&mut Self)>(
-        &mut self,
-        name: String,
-        def_id: LocalDefId,
-        sp: Span,
-        nested: F,
-    ) {
-        let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
-        if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
-            if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) {
-                return;
+                    if !stderr.is_empty() {
+                        eprintln!("stderr:\n{stderr}");
+                    }
+                }
             }
         }
 
-        let has_name = !name.is_empty();
-        if has_name {
-            self.collector.names.push(name);
-        }
-
-        // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
-        // anything else, this will combine them for us.
-        let attrs = Attributes::from_ast(ast_attrs);
-        if let Some(doc) = attrs.opt_doc_value() {
-            // Use the outermost invocation, so that doctest names come from where the docs were written.
-            let span = ast_attrs
-                .iter()
-                .find(|attr| attr.doc_str().is_some())
-                .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span))
-                .unwrap_or(DUMMY_SP);
-            self.collector.set_position(span);
-            markdown::find_testable_code(
-                &doc,
-                self.collector,
-                self.codes,
-                self.collector.enable_per_target_ignores,
-                Some(&crate::html::markdown::ExtraInfo::new(
-                    self.tcx,
-                    def_id.to_def_id(),
-                    span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
-                )),
-            );
-        }
-
-        nested(self);
-
-        if has_name {
-            self.collector.names.pop();
-        }
+        panic::resume_unwind(Box::new(()));
     }
+    Ok(())
 }
 
-impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.map
-    }
-
-    fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
-        let name = match &item.kind {
-            hir::ItemKind::Impl(impl_) => {
-                rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
-            }
-            _ => item.ident.to_string(),
-        };
-
-        self.visit_testable(name, item.owner_id.def_id, item.span, |this| {
-            intravisit::walk_item(this, item);
-        });
-    }
-
-    fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) {
-        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
-            intravisit::walk_trait_item(this, item);
-        });
-    }
-
-    fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) {
-        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
-            intravisit::walk_impl_item(this, item);
-        });
-    }
-
-    fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) {
-        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
-            intravisit::walk_foreign_item(this, item);
-        });
-    }
-
-    fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) {
-        self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| {
-            intravisit::walk_variant(this, v);
-        });
-    }
-
-    fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) {
-        self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| {
-            intravisit::walk_field_def(this, f);
-        });
+#[cfg(test)] // used in tests
+impl DoctestVisitor for Vec<usize> {
+    fn visit_test(&mut self, _test: String, _config: LangString, rel_line: MdRelLine) {
+        self.push(1 + rel_line.offset());
     }
 }
 
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
new file mode 100644
index 00000000000..599611407ed
--- /dev/null
+++ b/src/librustdoc/doctest/make.rs
@@ -0,0 +1,393 @@
+//! Logic for transforming the raw code given by the user into something actually
+//! runnable, e.g. by adding a `main` function if it doesn't already exist.
+
+use std::io;
+
+use rustc_ast as ast;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::emitter::stderr_destination;
+use rustc_errors::{ColorConfig, FatalError};
+use rustc_parse::new_parser_from_source_str;
+use rustc_parse::parser::attr::InnerAttrPolicy;
+use rustc_session::parse::ParseSess;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::SourceMap;
+use rustc_span::symbol::sym;
+use rustc_span::FileName;
+
+use super::GlobalTestOptions;
+
+/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
+/// lines before the test code begins as well as if the output stream supports colors or not.
+pub(crate) fn make_test(
+    s: &str,
+    crate_name: Option<&str>,
+    dont_insert_main: bool,
+    opts: &GlobalTestOptions,
+    edition: Edition,
+    test_id: Option<&str>,
+) -> (String, usize, bool) {
+    let (crate_attrs, everything_else, crates) = partition_source(s, edition);
+    let everything_else = everything_else.trim();
+    let mut line_offset = 0;
+    let mut prog = String::new();
+    let mut supports_color = false;
+
+    if opts.attrs.is_empty() {
+        // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some
+        // lints that are commonly triggered in doctests. The crate-level test attributes are
+        // commonly used to make tests fail in case they trigger warnings, so having this there in
+        // that case may cause some tests to pass when they shouldn't have.
+        prog.push_str("#![allow(unused)]\n");
+        line_offset += 1;
+    }
+
+    // Next, any attributes that came from the crate root via #![doc(test(attr(...)))].
+    for attr in &opts.attrs {
+        prog.push_str(&format!("#![{attr}]\n"));
+        line_offset += 1;
+    }
+
+    // Now push any outer attributes from the example, assuming they
+    // are intended to be crate attributes.
+    prog.push_str(&crate_attrs);
+    prog.push_str(&crates);
+
+    // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
+    // crate already is included.
+    let Ok((already_has_main, already_has_extern_crate)) =
+        check_for_main_and_extern_crate(crate_name, s.to_owned(), edition, &mut supports_color)
+    else {
+        // If the parser panicked due to a fatal error, pass the test code through unchanged.
+        // The error will be reported during compilation.
+        return (s.to_owned(), 0, false);
+    };
+
+    // Don't inject `extern crate std` because it's already injected by the
+    // compiler.
+    if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") {
+        if let Some(crate_name) = crate_name {
+            // Don't inject `extern crate` if the crate is never used.
+            // NOTE: this is terribly inaccurate because it doesn't actually
+            // parse the source, but only has false positives, not false
+            // negatives.
+            if s.contains(crate_name) {
+                // rustdoc implicitly inserts an `extern crate` item for the own crate
+                // which may be unused, so we need to allow the lint.
+                prog.push_str("#[allow(unused_extern_crates)]\n");
+
+                prog.push_str(&format!("extern crate r#{crate_name};\n"));
+                line_offset += 1;
+            }
+        }
+    }
+
+    // FIXME: This code cannot yet handle no_std test cases yet
+    if dont_insert_main || already_has_main || prog.contains("![no_std]") {
+        prog.push_str(everything_else);
+    } else {
+        let returns_result = everything_else.trim_end().ends_with("(())");
+        // Give each doctest main function a unique name.
+        // This is for example needed for the tooling around `-C instrument-coverage`.
+        let inner_fn_name = if let Some(test_id) = test_id {
+            format!("_doctest_main_{test_id}")
+        } else {
+            "_inner".into()
+        };
+        let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" };
+        let (main_pre, main_post) = if returns_result {
+            (
+                format!(
+                    "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n",
+                ),
+                format!("\n}} {inner_fn_name}().unwrap() }}"),
+            )
+        } else if test_id.is_some() {
+            (
+                format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n",),
+                format!("\n}} {inner_fn_name}() }}"),
+            )
+        } else {
+            ("fn main() {\n".into(), "\n}".into())
+        };
+        // Note on newlines: We insert a line/newline *before*, and *after*
+        // the doctest and adjust the `line_offset` accordingly.
+        // In the case of `-C instrument-coverage`, this means that the generated
+        // inner `main` function spans from the doctest opening codeblock to the
+        // closing one. For example
+        // /// ``` <- start of the inner main
+        // /// <- code under doctest
+        // /// ``` <- end of the inner main
+        line_offset += 1;
+
+        // add extra 4 spaces for each line to offset the code block
+        let content = if opts.insert_indent_space {
+            everything_else
+                .lines()
+                .map(|line| format!("    {}", line))
+                .collect::<Vec<String>>()
+                .join("\n")
+        } else {
+            everything_else.to_string()
+        };
+        prog.extend([&main_pre, content.as_str(), &main_post].iter().cloned());
+    }
+
+    debug!("final doctest:\n{prog}");
+
+    (prog, line_offset, supports_color)
+}
+
+fn check_for_main_and_extern_crate(
+    crate_name: Option<&str>,
+    source: String,
+    edition: Edition,
+    supports_color: &mut bool,
+) -> Result<(bool, bool), FatalError> {
+    let result = rustc_driver::catch_fatal_errors(|| {
+        rustc_span::create_session_if_not_set_then(edition, |_| {
+            use rustc_errors::emitter::{Emitter, HumanEmitter};
+            use rustc_errors::DiagCtxt;
+            use rustc_parse::parser::ForceCollect;
+            use rustc_span::source_map::FilePathMapping;
+
+            let filename = FileName::anon_source_code(&source);
+
+            // Any errors in parsing should also appear when the doctest is compiled for real, so just
+            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
+            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let fallback_bundle = rustc_errors::fallback_fluent_bundle(
+                rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
+                false,
+            );
+            *supports_color =
+                HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
+                    .supports_color();
+
+            let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+
+            // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
+            let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
+            let psess = ParseSess::with_dcx(dcx, sm);
+
+            let mut found_main = false;
+            let mut found_extern_crate = crate_name.is_none();
+            let mut found_macro = false;
+
+            let mut parser = match new_parser_from_source_str(&psess, filename, source.clone()) {
+                Ok(p) => p,
+                Err(errs) => {
+                    errs.into_iter().for_each(|err| err.cancel());
+                    return (found_main, found_extern_crate, found_macro);
+                }
+            };
+
+            loop {
+                match parser.parse_item(ForceCollect::No) {
+                    Ok(Some(item)) => {
+                        if !found_main
+                            && let ast::ItemKind::Fn(..) = item.kind
+                            && item.ident.name == sym::main
+                        {
+                            found_main = true;
+                        }
+
+                        if !found_extern_crate
+                            && let ast::ItemKind::ExternCrate(original) = item.kind
+                        {
+                            // This code will never be reached if `crate_name` is none because
+                            // `found_extern_crate` is initialized to `true` if it is none.
+                            let crate_name = crate_name.unwrap();
+
+                            match original {
+                                Some(name) => found_extern_crate = name.as_str() == crate_name,
+                                None => found_extern_crate = item.ident.as_str() == crate_name,
+                            }
+                        }
+
+                        if !found_macro && let ast::ItemKind::MacCall(..) = item.kind {
+                            found_macro = true;
+                        }
+
+                        if found_main && found_extern_crate {
+                            break;
+                        }
+                    }
+                    Ok(None) => break,
+                    Err(e) => {
+                        e.cancel();
+                        break;
+                    }
+                }
+
+                // The supplied item is only used for diagnostics,
+                // which are swallowed here anyway.
+                parser.maybe_consume_incorrect_semicolon(None);
+            }
+
+            // Reset errors so that they won't be reported as compiler bugs when dropping the
+            // dcx. Any errors in the tests will be reported when the test file is compiled,
+            // Note that we still need to cancel the errors above otherwise `Diag` will panic on
+            // drop.
+            psess.dcx.reset_err_count();
+
+            (found_main, found_extern_crate, found_macro)
+        })
+    });
+    let (already_has_main, already_has_extern_crate, found_macro) = result?;
+
+    // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
+    // see it. In that case, run the old text-based scan to see if they at least have a main
+    // function written inside a macro invocation. See
+    // https://github.com/rust-lang/rust/issues/56898
+    let already_has_main = if found_macro && !already_has_main {
+        source
+            .lines()
+            .map(|line| {
+                let comment = line.find("//");
+                if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line }
+            })
+            .any(|code| code.contains("fn main"))
+    } else {
+        already_has_main
+    };
+
+    Ok((already_has_main, already_has_extern_crate))
+}
+
+fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
+    if source.is_empty() {
+        // Empty content so nothing to check in here...
+        return true;
+    }
+    rustc_driver::catch_fatal_errors(|| {
+        rustc_span::create_session_if_not_set_then(edition, |_| {
+            use rustc_errors::emitter::HumanEmitter;
+            use rustc_errors::DiagCtxt;
+            use rustc_span::source_map::FilePathMapping;
+
+            let filename = FileName::anon_source_code(source);
+            // Any errors in parsing should also appear when the doctest is compiled for real, so just
+            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
+            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let fallback_bundle = rustc_errors::fallback_fluent_bundle(
+                rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
+                false,
+            );
+
+            let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
+
+            let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings();
+            let psess = ParseSess::with_dcx(dcx, sm);
+            let mut parser = match new_parser_from_source_str(&psess, filename, source.to_owned()) {
+                Ok(p) => p,
+                Err(errs) => {
+                    errs.into_iter().for_each(|err| err.cancel());
+                    // If there is an unclosed delimiter, an error will be returned by the
+                    // tokentrees.
+                    return false;
+                }
+            };
+            // If a parsing error happened, it's very likely that the attribute is incomplete.
+            if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) {
+                e.cancel();
+                return false;
+            }
+            true
+        })
+    })
+    .unwrap_or(false)
+}
+
+fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
+    #[derive(Copy, Clone, PartialEq)]
+    enum PartitionState {
+        Attrs,
+        Crates,
+        Other,
+    }
+    let mut state = PartitionState::Attrs;
+    let mut before = String::new();
+    let mut crates = String::new();
+    let mut after = String::new();
+
+    let mut mod_attr_pending = String::new();
+
+    for line in s.lines() {
+        let trimline = line.trim();
+
+        // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be
+        // shunted into "everything else"
+        match state {
+            PartitionState::Attrs => {
+                state = if trimline.starts_with("#![") {
+                    if !check_if_attr_is_complete(line, edition) {
+                        mod_attr_pending = line.to_owned();
+                    } else {
+                        mod_attr_pending.clear();
+                    }
+                    PartitionState::Attrs
+                } else if trimline.chars().all(|c| c.is_whitespace())
+                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
+                {
+                    PartitionState::Attrs
+                } else if trimline.starts_with("extern crate")
+                    || trimline.starts_with("#[macro_use] extern crate")
+                {
+                    PartitionState::Crates
+                } else {
+                    // First we check if the previous attribute was "complete"...
+                    if !mod_attr_pending.is_empty() {
+                        // If not, then we append the new line into the pending attribute to check
+                        // if this time it's complete...
+                        mod_attr_pending.push_str(line);
+                        if !trimline.is_empty()
+                            && check_if_attr_is_complete(&mod_attr_pending, edition)
+                        {
+                            // If it's complete, then we can clear the pending content.
+                            mod_attr_pending.clear();
+                        }
+                        // In any case, this is considered as `PartitionState::Attrs` so it's
+                        // prepended before rustdoc's inserts.
+                        PartitionState::Attrs
+                    } else {
+                        PartitionState::Other
+                    }
+                };
+            }
+            PartitionState::Crates => {
+                state = if trimline.starts_with("extern crate")
+                    || trimline.starts_with("#[macro_use] extern crate")
+                    || trimline.chars().all(|c| c.is_whitespace())
+                    || (trimline.starts_with("//") && !trimline.starts_with("///"))
+                {
+                    PartitionState::Crates
+                } else {
+                    PartitionState::Other
+                };
+            }
+            PartitionState::Other => {}
+        }
+
+        match state {
+            PartitionState::Attrs => {
+                before.push_str(line);
+                before.push('\n');
+            }
+            PartitionState::Crates => {
+                crates.push_str(line);
+                crates.push('\n');
+            }
+            PartitionState::Other => {
+                after.push_str(line);
+                after.push('\n');
+            }
+        }
+    }
+
+    debug!("before:\n{before}");
+    debug!("crates:\n{crates}");
+    debug!("after:\n{after}");
+
+    (before, after, crates)
+}
diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs
new file mode 100644
index 00000000000..b8ab7adb36e
--- /dev/null
+++ b/src/librustdoc/doctest/markdown.rs
@@ -0,0 +1,125 @@
+//! Doctest functionality used only for doctests in `.md` Markdown files.
+
+use std::fs::read_to_string;
+
+use rustc_span::FileName;
+use tempfile::tempdir;
+
+use super::{
+    generate_args_file, CreateRunnableDoctests, DoctestVisitor, GlobalTestOptions, ScrapedDoctest,
+};
+use crate::config::Options;
+use crate::html::markdown::{find_testable_code, ErrorCodes, LangString, MdRelLine};
+
+struct MdCollector {
+    tests: Vec<ScrapedDoctest>,
+    cur_path: Vec<String>,
+    filename: FileName,
+}
+
+impl DoctestVisitor for MdCollector {
+    fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) {
+        let filename = self.filename.clone();
+        // First line of Markdown is line 1.
+        let line = 1 + rel_line.offset();
+        self.tests.push(ScrapedDoctest {
+            filename,
+            line,
+            logical_path: self.cur_path.clone(),
+            langstr: config,
+            text: test,
+        });
+    }
+
+    fn visit_header(&mut self, name: &str, level: u32) {
+        // We use these headings as test names, so it's good if
+        // they're valid identifiers.
+        let name = name
+            .chars()
+            .enumerate()
+            .map(|(i, c)| {
+                if (i == 0 && rustc_lexer::is_id_start(c))
+                    || (i != 0 && rustc_lexer::is_id_continue(c))
+                {
+                    c
+                } else {
+                    '_'
+                }
+            })
+            .collect::<String>();
+
+        // Here we try to efficiently assemble the header titles into the
+        // test name in the form of `h1::h2::h3::h4::h5::h6`.
+        //
+        // Suppose that originally `self.cur_path` contains `[h1, h2, h3]`...
+        let level = level as usize;
+        if level <= self.cur_path.len() {
+            // ... Consider `level == 2`. All headers in the lower levels
+            // are irrelevant in this new level. So we should reset
+            // `self.names` to contain headers until <h2>, and replace that
+            // slot with the new name: `[h1, name]`.
+            self.cur_path.truncate(level);
+            self.cur_path[level - 1] = name;
+        } else {
+            // ... On the other hand, consider `level == 5`. This means we
+            // need to extend `self.names` to contain five headers. We fill
+            // in the missing level (<h4>) with `_`. Thus `self.names` will
+            // become `[h1, h2, h3, "_", name]`.
+            if level - 1 > self.cur_path.len() {
+                self.cur_path.resize(level - 1, "_".to_owned());
+            }
+            self.cur_path.push(name);
+        }
+    }
+}
+
+/// Runs any tests/code examples in the markdown file `options.input`.
+pub(crate) fn test(options: Options) -> Result<(), String> {
+    use rustc_session::config::Input;
+    let input_str = match &options.input {
+        Input::File(path) => {
+            read_to_string(&path).map_err(|err| format!("{}: {err}", path.display()))?
+        }
+        Input::Str { name: _, input } => input.clone(),
+    };
+
+    // Obviously not a real crate name, but close enough for purposes of doctests.
+    let crate_name = options.input.filestem().to_string();
+    let temp_dir =
+        tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?;
+    let args_file = temp_dir.path().join("rustdoc-cfgs");
+    generate_args_file(&args_file, &options)?;
+
+    let opts = GlobalTestOptions {
+        crate_name,
+        no_crate_inject: true,
+        insert_indent_space: false,
+        attrs: vec![],
+        args_file,
+    };
+
+    let mut md_collector = MdCollector {
+        tests: vec![],
+        cur_path: vec![],
+        filename: options
+            .input
+            .opt_path()
+            .map(ToOwned::to_owned)
+            .map(FileName::from)
+            .unwrap_or(FileName::Custom("input".to_owned())),
+    };
+    let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
+
+    find_testable_code(
+        &input_str,
+        &mut md_collector,
+        codes,
+        options.enable_per_target_ignores,
+        None,
+    );
+
+    let mut collector = CreateRunnableDoctests::new(options.clone(), opts);
+    md_collector.tests.into_iter().for_each(|t| collector.add_test(t));
+    crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests);
+    Ok(())
+}
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
new file mode 100644
index 00000000000..e6bef395fa9
--- /dev/null
+++ b/src/librustdoc/doctest/rust.rs
@@ -0,0 +1,198 @@
+//! Doctest functionality used only for doctests in `.rs` source files.
+
+use std::env;
+
+use rustc_data_structures::{fx::FxHashSet, sync::Lrc};
+use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
+use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID};
+use rustc_middle::hir::map::Map;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::ty::TyCtxt;
+use rustc_resolve::rustdoc::span_of_fragments;
+use rustc_session::Session;
+use rustc_span::source_map::SourceMap;
+use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP};
+
+use super::{DoctestVisitor, ScrapedDoctest};
+use crate::clean::{types::AttributesExt, Attributes};
+use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine};
+
+struct RustCollector {
+    source_map: Lrc<SourceMap>,
+    tests: Vec<ScrapedDoctest>,
+    cur_path: Vec<String>,
+    position: Span,
+}
+
+impl RustCollector {
+    fn get_filename(&self) -> FileName {
+        let filename = self.source_map.span_to_filename(self.position);
+        if let FileName::Real(ref filename) = filename
+            && let Ok(cur_dir) = env::current_dir()
+            && let Some(local_path) = filename.local_path()
+            && let Ok(path) = local_path.strip_prefix(&cur_dir)
+        {
+            return path.to_owned().into();
+        }
+        filename
+    }
+
+    fn get_base_line(&self) -> usize {
+        let sp_lo = self.position.lo().to_usize();
+        let loc = self.source_map.lookup_char_pos(BytePos(sp_lo as u32));
+        loc.line
+    }
+}
+
+impl DoctestVisitor for RustCollector {
+    fn visit_test(&mut self, test: String, config: LangString, rel_line: MdRelLine) {
+        let line = self.get_base_line() + rel_line.offset();
+        self.tests.push(ScrapedDoctest {
+            filename: self.get_filename(),
+            line,
+            logical_path: self.cur_path.clone(),
+            langstr: config,
+            text: test,
+        });
+    }
+
+    fn visit_header(&mut self, _name: &str, _level: u32) {}
+}
+
+pub(super) struct HirCollector<'a, 'tcx> {
+    sess: &'a Session,
+    map: Map<'tcx>,
+    codes: ErrorCodes,
+    tcx: TyCtxt<'tcx>,
+    enable_per_target_ignores: bool,
+    collector: RustCollector,
+}
+
+impl<'a, 'tcx> HirCollector<'a, 'tcx> {
+    pub fn new(
+        sess: &'a Session,
+        map: Map<'tcx>,
+        codes: ErrorCodes,
+        enable_per_target_ignores: bool,
+        tcx: TyCtxt<'tcx>,
+    ) -> Self {
+        let collector = RustCollector {
+            source_map: sess.psess.clone_source_map(),
+            cur_path: vec![],
+            position: DUMMY_SP,
+            tests: vec![],
+        };
+        Self { sess, map, codes, enable_per_target_ignores, tcx, collector }
+    }
+
+    pub fn collect_crate(mut self) -> Vec<ScrapedDoctest> {
+        let tcx = self.tcx;
+        self.visit_testable("".to_string(), CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID), |this| {
+            tcx.hir().walk_toplevel_module(this)
+        });
+        self.collector.tests
+    }
+}
+
+impl<'a, 'tcx> HirCollector<'a, 'tcx> {
+    fn visit_testable<F: FnOnce(&mut Self)>(
+        &mut self,
+        name: String,
+        def_id: LocalDefId,
+        sp: Span,
+        nested: F,
+    ) {
+        let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id));
+        if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
+            if !cfg.matches(&self.sess.psess, Some(self.tcx.features())) {
+                return;
+            }
+        }
+
+        let has_name = !name.is_empty();
+        if has_name {
+            self.collector.cur_path.push(name);
+        }
+
+        // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
+        // anything else, this will combine them for us.
+        let attrs = Attributes::from_ast(ast_attrs);
+        if let Some(doc) = attrs.opt_doc_value() {
+            // Use the outermost invocation, so that doctest names come from where the docs were written.
+            let span = ast_attrs
+                .iter()
+                .find(|attr| attr.doc_str().is_some())
+                .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span))
+                .unwrap_or(DUMMY_SP);
+            self.collector.position = span;
+            markdown::find_testable_code(
+                &doc,
+                &mut self.collector,
+                self.codes,
+                self.enable_per_target_ignores,
+                Some(&crate::html::markdown::ExtraInfo::new(
+                    self.tcx,
+                    def_id.to_def_id(),
+                    span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
+                )),
+            );
+        }
+
+        nested(self);
+
+        if has_name {
+            self.collector.cur_path.pop();
+        }
+    }
+}
+
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for HirCollector<'a, 'tcx> {
+    type NestedFilter = nested_filter::All;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.map
+    }
+
+    fn visit_item(&mut self, item: &'tcx hir::Item<'_>) {
+        let name = match &item.kind {
+            hir::ItemKind::Impl(impl_) => {
+                rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
+            }
+            _ => item.ident.to_string(),
+        };
+
+        self.visit_testable(name, item.owner_id.def_id, item.span, |this| {
+            intravisit::walk_item(this, item);
+        });
+    }
+
+    fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'_>) {
+        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
+            intravisit::walk_trait_item(this, item);
+        });
+    }
+
+    fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'_>) {
+        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
+            intravisit::walk_impl_item(this, item);
+        });
+    }
+
+    fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'_>) {
+        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
+            intravisit::walk_foreign_item(this, item);
+        });
+    }
+
+    fn visit_variant(&mut self, v: &'tcx hir::Variant<'_>) {
+        self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| {
+            intravisit::walk_variant(this, v);
+        });
+    }
+
+    fn visit_field_def(&mut self, f: &'tcx hir::FieldDef<'_>) {
+        self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| {
+            intravisit::walk_field_def(this, f);
+        });
+    }
+}
diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs
index 9629acb31eb..9124ec63267 100644
--- a/src/librustdoc/doctest/tests.rs
+++ b/src/librustdoc/doctest/tests.rs
@@ -1,10 +1,23 @@
+use std::path::PathBuf;
+
 use super::{make_test, GlobalTestOptions};
 use rustc_span::edition::DEFAULT_EDITION;
 
+/// Default [`GlobalTestOptions`] for these unit tests.
+fn default_global_opts(crate_name: impl Into<String>) -> GlobalTestOptions {
+    GlobalTestOptions {
+        crate_name: crate_name.into(),
+        no_crate_inject: false,
+        insert_indent_space: false,
+        attrs: vec![],
+        args_file: PathBuf::new(),
+    }
+}
+
 #[test]
 fn make_test_basic() {
     //basic use: wraps with `fn main`, adds `#![allow(unused)]`
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("");
     let input = "assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
 fn main() {
@@ -19,7 +32,7 @@ assert_eq!(2+2, 4);
 fn make_test_crate_name_no_use() {
     // If you give a crate name but *don't* use it within the test, it won't bother inserting
     // the `extern crate` statement.
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("asdf");
     let input = "assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
 fn main() {
@@ -34,7 +47,7 @@ assert_eq!(2+2, 4);
 fn make_test_crate_name() {
     // If you give a crate name and use it within the test, it will insert an `extern crate`
     // statement before `fn main`.
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("asdf");
     let input = "use asdf::qwop;
 assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
@@ -53,8 +66,7 @@ assert_eq!(2+2, 4);
 fn make_test_no_crate_inject() {
     // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip
     // adding it anyway.
-    let opts =
-        GlobalTestOptions { no_crate_inject: true, attrs: vec![], insert_indent_space: false };
+    let opts = GlobalTestOptions { no_crate_inject: true, ..default_global_opts("asdf") };
     let input = "use asdf::qwop;
 assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
@@ -72,7 +84,7 @@ fn make_test_ignore_std() {
     // Even if you include a crate name, and use it in the doctest, we still won't include an
     // `extern crate` statement if the crate is "std" -- that's included already by the
     // compiler!
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("std");
     let input = "use std::*;
 assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
@@ -89,7 +101,7 @@ assert_eq!(2+2, 4);
 fn make_test_manual_extern_crate() {
     // When you manually include an `extern crate` statement in your doctest, `make_test`
     // assumes you've included one for your own crate too.
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("asdf");
     let input = "extern crate asdf;
 use asdf::qwop;
 assert_eq!(2+2, 4);";
@@ -106,7 +118,7 @@ assert_eq!(2+2, 4);
 
 #[test]
 fn make_test_manual_extern_crate_with_macro_use() {
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("asdf");
     let input = "#[macro_use] extern crate asdf;
 use asdf::qwop;
 assert_eq!(2+2, 4);";
@@ -125,7 +137,7 @@ assert_eq!(2+2, 4);
 fn make_test_opts_attrs() {
     // If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use
     // those instead of the stock `#![allow(unused)]`.
-    let mut opts = GlobalTestOptions::default();
+    let mut opts = default_global_opts("asdf");
     opts.attrs.push("feature(sick_rad)".to_string());
     let input = "use asdf::qwop;
 assert_eq!(2+2, 4);";
@@ -159,7 +171,7 @@ assert_eq!(2+2, 4);
 fn make_test_crate_attrs() {
     // Including inner attributes in your doctest will apply them to the whole "crate", pasting
     // them outside the generated main function.
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("");
     let input = "#![feature(sick_rad)]
 assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
@@ -175,7 +187,7 @@ assert_eq!(2+2, 4);
 #[test]
 fn make_test_with_main() {
     // Including your own `fn main` wrapper lets the test use it verbatim.
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("");
     let input = "fn main() {
     assert_eq!(2+2, 4);
 }";
@@ -191,7 +203,7 @@ fn main() {
 #[test]
 fn make_test_fake_main() {
     // ... but putting it in a comment will still provide a wrapper.
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("");
     let input = "//Ceci n'est pas une `fn main`
 assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
@@ -207,7 +219,7 @@ assert_eq!(2+2, 4);
 #[test]
 fn make_test_dont_insert_main() {
     // Even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper.
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("");
     let input = "//Ceci n'est pas une `fn main`
 assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
@@ -219,8 +231,8 @@ assert_eq!(2+2, 4);"
 }
 
 #[test]
-fn make_test_issues_21299_33731() {
-    let opts = GlobalTestOptions::default();
+fn make_test_issues_21299() {
+    let opts = default_global_opts("");
 
     let input = "// fn main
 assert_eq!(2+2, 4);";
@@ -234,6 +246,11 @@ assert_eq!(2+2, 4);
 
     let (output, len, _) = make_test(input, None, false, &opts, DEFAULT_EDITION, None);
     assert_eq!((output, len), (expected, 2));
+}
+
+#[test]
+fn make_test_issues_33731() {
+    let opts = default_global_opts("asdf");
 
     let input = "extern crate hella_qwop;
 assert_eq!(asdf::foo, 4);";
@@ -253,7 +270,7 @@ assert_eq!(asdf::foo, 4);
 
 #[test]
 fn make_test_main_in_macro() {
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("my_crate");
     let input = "#[macro_use] extern crate my_crate;
 test_wrapper! {
     fn main() {}
@@ -272,7 +289,7 @@ test_wrapper! {
 #[test]
 fn make_test_returns_result() {
     // creates an inner function and unwraps it
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("");
     let input = "use std::io;
 let mut input = String::new();
 io::stdin().read_line(&mut input)?;
@@ -292,7 +309,7 @@ Ok::<(), io:Error>(())
 #[test]
 fn make_test_named_wrapper() {
     // creates an inner function with a specific name
-    let opts = GlobalTestOptions::default();
+    let opts = default_global_opts("");
     let input = "assert_eq!(2+2, 4);";
     let expected = "#![allow(unused)]
 fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() {
@@ -307,8 +324,7 @@ assert_eq!(2+2, 4);
 #[test]
 fn make_test_insert_extra_space() {
     // will insert indent spaces in the code block if `insert_indent_space` is true
-    let opts =
-        GlobalTestOptions { no_crate_inject: false, attrs: vec![], insert_indent_space: true };
+    let opts = GlobalTestOptions { insert_indent_space: true, ..default_global_opts("") };
     let input = "use std::*;
 assert_eq!(2+2, 4);
 eprintln!(\"hello anan\");
@@ -327,8 +343,7 @@ fn main() {
 #[test]
 fn make_test_insert_extra_space_fn_main() {
     // if input already has a fn main, it should insert a space before it
-    let opts =
-        GlobalTestOptions { no_crate_inject: false, attrs: vec![], insert_indent_space: true };
+    let opts = GlobalTestOptions { insert_indent_space: true, ..default_global_opts("") };
     let input = "use std::*;
 fn main() {
     assert_eq!(2+2, 4);
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 11cc81700ff..bae929c64ea 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -39,6 +39,7 @@ use std::collections::VecDeque;
 use std::fmt::Write;
 use std::iter::Peekable;
 use std::ops::{ControlFlow, Range};
+use std::path::PathBuf;
 use std::str::{self, CharIndices};
 use std::sync::OnceLock;
 
@@ -287,8 +288,15 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
                 .collect::<String>();
             let krate = krate.as_ref().map(|s| s.as_str());
 
-            let mut opts: GlobalTestOptions = Default::default();
-            opts.insert_indent_space = true;
+            // FIXME: separate out the code to make a code block into runnable code
+            //        from the complicated doctest logic
+            let opts = GlobalTestOptions {
+                crate_name: krate.map(String::from).unwrap_or_default(),
+                no_crate_inject: false,
+                insert_indent_space: true,
+                attrs: vec![],
+                args_file: PathBuf::new(),
+            };
             let (test, _, _) = doctest::make_test(&test, krate, false, &opts, edition, None);
             let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };
 
@@ -710,7 +718,29 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
     }
 }
 
-pub(crate) fn find_testable_code<T: doctest::Tester>(
+/// A newtype that represents a relative line number in Markdown.
+///
+/// In other words, this represents an offset from the first line of Markdown
+/// in a doc comment or other source. If the first Markdown line appears on line 32,
+/// and the `MdRelLine` is 3, then the absolute line for this one is 35. I.e., it's
+/// a zero-based offset.
+pub(crate) struct MdRelLine {
+    offset: usize,
+}
+
+impl MdRelLine {
+    /// See struct docs.
+    pub(crate) const fn new(offset: usize) -> Self {
+        Self { offset }
+    }
+
+    /// See struct docs.
+    pub(crate) const fn offset(self) -> usize {
+        self.offset
+    }
+}
+
+pub(crate) fn find_testable_code<T: doctest::DoctestVisitor>(
     doc: &str,
     tests: &mut T,
     error_codes: ErrorCodes,
@@ -720,7 +750,7 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
     find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false)
 }
 
-pub(crate) fn find_codes<T: doctest::Tester>(
+pub(crate) fn find_codes<T: doctest::DoctestVisitor>(
     doc: &str,
     tests: &mut T,
     error_codes: ErrorCodes,
@@ -772,8 +802,8 @@ pub(crate) fn find_codes<T: doctest::Tester>(
                 if nb_lines != 0 && !&doc[prev_offset..offset.start].ends_with('\n') {
                     nb_lines -= 1;
                 }
-                let line = tests.get_line() + nb_lines + 1;
-                tests.add_test(text, block_info, line);
+                let line = MdRelLine::new(nb_lines);
+                tests.visit_test(text, block_info, line);
                 prev_offset = offset.start;
             }
             Event::Start(Tag::Heading(level, _, _)) => {
@@ -781,7 +811,7 @@ pub(crate) fn find_codes<T: doctest::Tester>(
             }
             Event::Text(ref s) if register_header.is_some() => {
                 let level = register_header.unwrap();
-                tests.register_header(s, level);
+                tests.visit_header(s, level);
                 register_header = None;
             }
             _ => {}
diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js
index a1e9cc6dfa1..fbb096fe9c7 100644
--- a/src/librustdoc/html/static/.eslintrc.js
+++ b/src/librustdoc/html/static/.eslintrc.js
@@ -5,7 +5,7 @@ module.exports = {
     },
     "extends": "eslint:recommended",
     "parserOptions": {
-        "ecmaVersion": 8,
+        "ecmaVersion": 2019,
         "sourceType": "module"
     },
     "rules": {
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index 8cebce7ae86..6fd60d6cc34 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -41,8 +41,9 @@ let ParserState;
  *     foundElems: number,
  *     totalElems: number,
  *     literalSearch: boolean,
- *     corrections: Array<{from: string, to: integer}>,
+ *     corrections: Array<{from: string, to: integer}> | null,
  *     typeFingerprint: Uint32Array,
+ *     error: Array<string> | null,
  * }}
  */
 let ParsedQuery;
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 76a6fc9008e..8ac4b53673f 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -89,6 +89,10 @@ const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
 // of permutations we need to check.
 const UNBOXING_LIMIT = 5;
 
+// used for search query verification
+const REGEX_IDENT = /\p{ID_Start}\p{ID_Continue}*|_\p{ID_Continue}+/uy;
+const REGEX_INVALID_TYPE_FILTER = /[^a-z]/ui;
+
 // In the search display, allows to switch between tabs.
 function printTab(nb) {
     let iter = 0;
@@ -410,18 +414,21 @@ function initSearch(rawSearchIndex) {
     }
 
     /**
-     * Returns `true` if the given `c` character is valid for an ident.
+     * If the current parser position is at the beginning of an identifier,
+     * move the position to the end of it and return `true`. Otherwise, return `false`.
      *
-     * @param {string} c
+     * @param {ParserState} parserState
      *
      * @return {boolean}
      */
-    function isIdentCharacter(c) {
-        return (
-            c === "_" ||
-            (c >= "0" && c <= "9") ||
-            (c >= "a" && c <= "z") ||
-            (c >= "A" && c <= "Z"));
+    function consumeIdent(parserState) {
+        REGEX_IDENT.lastIndex = parserState.pos;
+        const match = parserState.userQuery.match(REGEX_IDENT);
+        if (match) {
+            parserState.pos += match[0].length;
+            return true;
+        }
+        return false;
     }
 
     /**
@@ -618,70 +625,62 @@ function initSearch(rawSearchIndex) {
      * @return {integer}
      */
     function getIdentEndPosition(parserState) {
-        const start = parserState.pos;
+        let afterIdent = consumeIdent(parserState);
         let end = parserState.pos;
-        let foundExclamation = -1;
+        let macroExclamation = -1;
         while (parserState.pos < parserState.length) {
             const c = parserState.userQuery[parserState.pos];
-            if (!isIdentCharacter(c)) {
-                if (c === "!") {
-                    if (foundExclamation !== -1) {
-                        throw ["Cannot have more than one ", "!", " in an ident"];
-                    } else if (parserState.pos + 1 < parserState.length &&
-                        isIdentCharacter(parserState.userQuery[parserState.pos + 1])
-                    ) {
+            if (c === "!") {
+                if (macroExclamation !== -1) {
+                    throw ["Cannot have more than one ", "!", " in an ident"];
+                } else if (parserState.pos + 1 < parserState.length) {
+                    const pos = parserState.pos;
+                    parserState.pos++;
+                    const beforeIdent = consumeIdent(parserState);
+                    parserState.pos = pos;
+                    if (beforeIdent) {
                         throw ["Unexpected ", "!", ": it can only be at the end of an ident"];
                     }
-                    foundExclamation = parserState.pos;
-                } else if (isPathSeparator(c)) {
-                    if (c === ":") {
-                        if (!isPathStart(parserState)) {
+                }
+                if (afterIdent) macroExclamation = parserState.pos;
+            } else if (isPathSeparator(c)) {
+                if (c === ":") {
+                    if (!isPathStart(parserState)) {
+                        break;
+                    }
+                    // Skip current ":".
+                    parserState.pos += 1;
+                } else {
+                    while (parserState.pos + 1 < parserState.length) {
+                        const next_c = parserState.userQuery[parserState.pos + 1];
+                        if (next_c !== " ") {
                             break;
                         }
-                        // Skip current ":".
                         parserState.pos += 1;
-                    } else {
-                        while (parserState.pos + 1 < parserState.length) {
-                            const next_c = parserState.userQuery[parserState.pos + 1];
-                            if (next_c !== " ") {
-                                break;
-                            }
-                            parserState.pos += 1;
-                        }
                     }
-                    if (foundExclamation !== -1) {
-                        if (foundExclamation !== start &&
-                            isIdentCharacter(parserState.userQuery[foundExclamation - 1])
-                        ) {
-                            throw ["Cannot have associated items in macros"];
-                        } else {
-                            // while the never type has no associated macros, we still
-                            // can parse a path like that
-                            foundExclamation = -1;
-                        }
-                    }
-                } else if (
-                    c === "[" ||
-                    c === "(" ||
-                    isEndCharacter(c) ||
-                    isSpecialStartCharacter(c) ||
-                    isSeparatorCharacter(c)
-                ) {
-                    break;
-                } else if (parserState.pos > 0) {
-                    throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]];
-                } else {
-                    throw ["Unexpected ", c];
                 }
+                if (macroExclamation !== -1) {
+                    throw ["Cannot have associated items in macros"];
+                }
+            } else if (
+                c === "[" ||
+                c === "(" ||
+                isEndCharacter(c) ||
+                isSpecialStartCharacter(c) ||
+                isSeparatorCharacter(c)
+            ) {
+                break;
+            } else if (parserState.pos > 0) {
+                throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1],
+                    " (not a valid identifier)"];
+            } else {
+                throw ["Unexpected ", c, " (not a valid identifier)"];
             }
             parserState.pos += 1;
+            afterIdent = consumeIdent(parserState);
             end = parserState.pos;
         }
-        // if start == end - 1, we got the never type
-        if (foundExclamation !== -1 &&
-            foundExclamation !== start &&
-            isIdentCharacter(parserState.userQuery[foundExclamation - 1])
-        ) {
+        if (macroExclamation !== -1) {
             if (parserState.typeFilter === null) {
                 parserState.typeFilter = "macro";
             } else if (parserState.typeFilter !== "macro") {
@@ -693,7 +692,7 @@ function initSearch(rawSearchIndex) {
                     " both specified",
                 ];
             }
-            end = foundExclamation;
+            end = macroExclamation;
         }
         return end;
     }
@@ -1071,16 +1070,15 @@ function initSearch(rawSearchIndex) {
     function checkExtraTypeFilterCharacters(start, parserState) {
         const query = parserState.userQuery.slice(start, parserState.pos).trim();
 
-        for (const c in query) {
-            if (!isIdentCharacter(query[c])) {
-                throw [
-                    "Unexpected ",
-                    query[c],
-                    " in type filter (before ",
-                    ":",
-                    ")",
-                ];
-            }
+        const match = query.match(REGEX_INVALID_TYPE_FILTER);
+        if (match) {
+            throw [
+                "Unexpected ",
+                match[0],
+                " in type filter (before ",
+                ":",
+                ")",
+            ];
         }
     }
 
@@ -2127,7 +2125,7 @@ function initSearch(rawSearchIndex) {
             };
         }
 
-        function handleAliases(ret, query, filterCrates, currentCrate) {
+        async function handleAliases(ret, query, filterCrates, currentCrate) {
             const lowerQuery = query.toLowerCase();
             // We separate aliases and crate aliases because we want to have current crate
             // aliases to be before the others in the displayed results.
@@ -2163,6 +2161,15 @@ function initSearch(rawSearchIndex) {
             crateAliases.sort(sortFunc);
             aliases.sort(sortFunc);
 
+            const fetchDesc = alias => {
+                return searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ?
+                    "" : searchState.loadDesc(alias);
+            };
+            const [crateDescs, descs] = await Promise.all([
+                Promise.all(crateAliases.map(fetchDesc)),
+                Promise.all(aliases.map(fetchDesc)),
+            ]);
+
             const pushFunc = alias => {
                 alias.alias = query;
                 const res = buildHrefAndPath(alias);
@@ -2176,7 +2183,13 @@ function initSearch(rawSearchIndex) {
                 }
             };
 
+            aliases.forEach((alias, i) => {
+                alias.desc = descs[i];
+            });
             aliases.forEach(pushFunc);
+            crateAliases.forEach((alias, i) => {
+                alias.desc = crateDescs[i];
+            });
             crateAliases.forEach(pushFunc);
         }
 
@@ -2538,7 +2551,8 @@ function initSearch(rawSearchIndex) {
             sorted_returned,
             sorted_others,
             parsedQuery);
-        handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate);
+        await handleAliases(ret, parsedQuery.original.replace(/"/g, ""),
+            filterCrates, currentCrate);
         await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => {
             const descs = await Promise.all(list.map(result => {
                 return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ?
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index c0d2f9cfaf9..3b6bddf263a 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -728,7 +728,7 @@ fn main_args(
         core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
 
     match (options.should_test, options.markdown_input()) {
-        (true, Some(_)) => return wrap_return(&diag, markdown::test(options)),
+        (true, Some(_)) => return wrap_return(&diag, doctest::test_markdown(options)),
         (true, None) => return doctest::run(&diag, options),
         (false, Some(input)) => {
             let input = input.to_owned();
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index bcc5a37618a..a98f81d011e 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -3,18 +3,12 @@ use std::fs::{create_dir_all, read_to_string, File};
 use std::io::prelude::*;
 use std::path::Path;
 
-use tempfile::tempdir;
-
 use rustc_span::edition::Edition;
-use rustc_span::DUMMY_SP;
 
-use crate::config::{Options, RenderOptions};
-use crate::doctest::{generate_args_file, Collector, GlobalTestOptions};
+use crate::config::RenderOptions;
 use crate::html::escape::Escape;
 use crate::html::markdown;
-use crate::html::markdown::{
-    find_testable_code, ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc,
-};
+use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, MarkdownWithToc};
 
 /// Separate any lines at the start of the file that begin with `# ` or `%`.
 fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) {
@@ -137,41 +131,3 @@ pub(crate) fn render<P: AsRef<Path>>(
         Ok(_) => Ok(()),
     }
 }
-
-/// Runs any tests/code examples in the markdown file `input`.
-pub(crate) fn test(options: Options) -> Result<(), String> {
-    use rustc_session::config::Input;
-    let input_str = match &options.input {
-        Input::File(path) => {
-            read_to_string(&path).map_err(|err| format!("{}: {err}", path.display()))?
-        }
-        Input::Str { name: _, input } => input.clone(),
-    };
-
-    let mut opts = GlobalTestOptions::default();
-    opts.no_crate_inject = true;
-
-    let temp_dir =
-        tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?;
-    let file_path = temp_dir.path().join("rustdoc-cfgs");
-    generate_args_file(&file_path, &options)?;
-
-    let mut collector = Collector::new(
-        options.input.filestem().to_string(),
-        options.clone(),
-        true,
-        opts,
-        None,
-        options.input.opt_path().map(ToOwned::to_owned),
-        options.enable_per_target_ignores,
-        file_path,
-    );
-    collector.set_position(DUMMY_SP);
-    let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
-
-    // For markdown files, custom code classes will be disabled until the feature is enabled by default.
-    find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);
-
-    crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests);
-    Ok(())
-}
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index d53eac0bccb..0437f5e5fd8 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -10,7 +10,7 @@ use crate::clean;
 use crate::clean::utils::inherits_doc_hidden;
 use crate::clean::*;
 use crate::core::DocContext;
-use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString};
+use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString, MdRelLine};
 use crate::visit::DocVisitor;
 use rustc_hir as hir;
 use rustc_middle::lint::LintLevelSource;
@@ -44,8 +44,8 @@ pub(crate) struct Tests {
     pub(crate) found_tests: usize,
 }
 
-impl crate::doctest::Tester for Tests {
-    fn add_test(&mut self, _: String, config: LangString, _: usize) {
+impl crate::doctest::DoctestVisitor for Tests {
+    fn visit_test(&mut self, _: String, config: LangString, _: MdRelLine) {
         if config.rust && config.ignore == Ignore::None {
             self.found_tests += 1;
         }
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 34a6a87d8a2330d8c9d578f927489689328a652
+Subproject b1feb75d062444e2cee8b3d2aaa95309d65e9cc
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index cd88ccd87cf..5c9cad2b45d 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -6,7 +6,7 @@ use crate::{clip, is_direct_expn_of, sext, unsext};
 use rustc_ast::ast::{self, LitFloatType, LitKind};
 use rustc_data_structures::sync::Lrc;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
+use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
 use rustc_lexer::tokenize;
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{alloc_range, Scalar};
@@ -412,7 +412,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
     /// Simple constant folding: Insert an expression, get a constant or none.
     pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
         match e.kind {
-            ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr(e),
+            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e),
             ExprKind::Path(ref qpath) => {
                 self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
                     let result = mir_to_const(this.lcx, result)?;
@@ -490,7 +490,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
     /// leaves the local crate.
     pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
         match e.kind {
-            ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr_is_empty(e),
+            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr_is_empty(e),
             ExprKind::Path(ref qpath) => {
                 if !self
                     .typeck_results
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index c649c179468..36634817fc9 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -295,7 +295,7 @@ impl HirEqInterExpr<'_, '_, '_> {
                 self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
             },
             (&ExprKind::Closure(_l), &ExprKind::Closure(_r)) => false,
-            (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_expr(lb, rb),
+            (&ExprKind::ConstBlock(lb), &ExprKind::ConstBlock(rb)) => self.eq_body(lb.body, rb.body),
             (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
                 both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
             },
@@ -769,8 +769,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 // closures inherit TypeckResults
                 self.hash_expr(self.cx.tcx.hir().body(body).value);
             },
-            ExprKind::ConstBlock(l_id) => {
-                self.hash_expr(l_id);
+            ExprKind::ConstBlock(ref l_id) => {
+                self.hash_body(l_id.body);
             },
             ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
                 self.hash_expr(e);
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
index df14ff396f6..8039c0bfa24 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
@@ -1,35 +1,11 @@
 error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:188:36
-   |
-LL |     let _ = const { let mut n = 1; n += 1; n };
-   |                                    ^^^^^^
-   |
-   = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]`
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:191:40
-   |
-LL |     let _ = const { let mut n = 1; n = n + 1; n };
-   |                                        ^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:194:40
-   |
-LL |     let _ = const { let mut n = 1; n = 1 + n; n };
-   |                                        ^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
-  --> tests/ui/arithmetic_side_effects.rs:200:59
-   |
-LL |     let _ = const { let mut n = 1; n = -1; n = -(-1); n = -n; n };
-   |                                                           ^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
   --> tests/ui/arithmetic_side_effects.rs:304:5
    |
 LL |     _n += 1;
    |     ^^^^^^^
+   |
+   = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]`
 
 error: arithmetic operation that can potentially result in unexpected side-effects
   --> tests/ui/arithmetic_side_effects.rs:305:5
@@ -751,5 +727,5 @@ error: arithmetic operation that can potentially result in unexpected side-effec
 LL |     one.sub_assign(1);
    |     ^^^^^^^^^^^^^^^^^
 
-error: aborting due to 125 previous errors
+error: aborting due to 121 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
index 84dac431169..05d5b3d10ea 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
@@ -16,7 +16,7 @@ fn foo(
 
 fn skip_on_statements() {
     #[rustfmt::skip]
-    { 5+3; }
+    5+3;
 }
 
 #[rustfmt::skip]
@@ -33,11 +33,11 @@ mod foo {
 #[clippy::msrv = "1.29"]
 fn msrv_1_29() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 1+29; }
+    1+29;
 }
 
 #[clippy::msrv = "1.30"]
 fn msrv_1_30() {
     #[rustfmt::skip]
-    { 1+30; }
+    1+30;
 }
diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
index 4ab5c70e13b..bc29e20210e 100644
--- a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
+++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
@@ -16,7 +16,7 @@ fn foo(
 
 fn skip_on_statements() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 5+3; }
+    5+3;
 }
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
@@ -33,11 +33,11 @@ mod foo {
 #[clippy::msrv = "1.29"]
 fn msrv_1_29() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 1+29; }
+    1+29;
 }
 
 #[clippy::msrv = "1.30"]
 fn msrv_1_30() {
     #[cfg_attr(rustfmt, rustfmt::skip)]
-    { 1+30; }
+    1+30;
 }
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
index ec4ae2ea13c..6ede7bfcd9f 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
@@ -45,8 +45,8 @@ fn std_instead_of_core() {
 
     let _ = std::env!("PATH");
 
-    // do not lint until `error_in_core` is stable
-    use std::error::Error;
+    use core::error::Error;
+    //~^ ERROR: used import from `std` instead of `core`
 
     // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator`
     use core::iter::Iterator;
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
index c12c459c7eb..e22b4f61f3e 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.rs
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -45,8 +45,8 @@ fn std_instead_of_core() {
 
     let _ = std::env!("PATH");
 
-    // do not lint until `error_in_core` is stable
     use std::error::Error;
+    //~^ ERROR: used import from `std` instead of `core`
 
     // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator`
     use std::iter::Iterator;
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
index 8f920511cc5..22cb9db7050 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
@@ -50,6 +50,12 @@ LL |     let cell_absolute = ::std::cell::Cell::new(8u32);
    |                           ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
+  --> tests/ui/std_instead_of_core.rs:48:9
+   |
+LL |     use std::error::Error;
+   |         ^^^ help: consider importing the item from `core`: `core`
+
+error: used import from `std` instead of `core`
   --> tests/ui/std_instead_of_core.rs:52:9
    |
 LL |     use std::iter::Iterator;
@@ -79,5 +85,5 @@ LL |     use alloc::slice::from_ref;
    = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]`
 
-error: aborting due to 12 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 28c766f7e17..dbe016b8305 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -15,7 +15,7 @@ use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::json;
 use crate::read2::{read2_abbreviated, Truncated};
-use crate::util::{add_dylib_path, dylib_env_var, logv, PathBufExt};
+use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, PathBufExt};
 use crate::ColorConfig;
 use colored::Colorize;
 use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile};
@@ -3471,6 +3471,21 @@ impl<'test> TestCx<'test> {
         let rmake_out_dir = base_dir.join("rmake_out");
         create_dir_all(&rmake_out_dir).unwrap();
 
+        // Copy all input files (apart from rmake.rs) to the temporary directory,
+        // so that the input directory structure from `tests/run-make/<test>` is mirrored
+        // to the `rmake_out` directory.
+        for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) {
+            let path = path.unwrap().path().to_path_buf();
+            if path.file_name().is_some_and(|s| s != "rmake.rs") {
+                let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap());
+                if path.is_dir() {
+                    copy_dir_all(&path, target).unwrap();
+                } else {
+                    fs::copy(&path, target).unwrap();
+                }
+            }
+        }
+
         // HACK: assume stageN-target, we only want stageN.
         let stage = self.config.stage_id.split('-').next().unwrap();
 
@@ -3528,21 +3543,13 @@ impl<'test> TestCx<'test> {
             .arg(&self.testpaths.file.join("rmake.rs"))
             .env("TARGET", &self.config.target)
             .env("PYTHON", &self.config.python)
-            .env("S", &src_root)
             .env("RUST_BUILD_STAGE", &self.config.stage_id)
             .env("RUSTC", cwd.join(&self.config.rustc_path))
-            .env("TMPDIR", &rmake_out_dir)
             .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
             .env(dylib_env_var(), &host_dylib_env_paths)
             .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
             .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
-            .env("LLVM_COMPONENTS", &self.config.llvm_components)
-            // We for sure don't want these tests to run in parallel, so make
-            // sure they don't have access to these vars if we run via `make`
-            // at the top level
-            .env_remove("MAKEFLAGS")
-            .env_remove("MFLAGS")
-            .env_remove("CARGO_MAKEFLAGS");
+            .env("LLVM_COMPONENTS", &self.config.llvm_components);
 
         if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() {
             let mut stage0_sysroot = build_root.clone();
@@ -3572,7 +3579,7 @@ impl<'test> TestCx<'test> {
         let target_rpath_env_path = env::join_paths(target_rpath_env_path).unwrap();
 
         let mut cmd = Command::new(&recipe_bin);
-        cmd.current_dir(&self.testpaths.file)
+        cmd.current_dir(&rmake_out_dir)
             .stdout(Stdio::piped())
             .stderr(Stdio::piped())
             .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
@@ -3580,19 +3587,12 @@ impl<'test> TestCx<'test> {
             .env(dylib_env_var(), &dylib_env_paths)
             .env("TARGET", &self.config.target)
             .env("PYTHON", &self.config.python)
-            .env("S", &src_root)
+            .env("SOURCE_ROOT", &src_root)
             .env("RUST_BUILD_STAGE", &self.config.stage_id)
             .env("RUSTC", cwd.join(&self.config.rustc_path))
-            .env("TMPDIR", &rmake_out_dir)
             .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
             .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
-            .env("LLVM_COMPONENTS", &self.config.llvm_components)
-            // We for sure don't want these tests to run in parallel, so make
-            // sure they don't have access to these vars if we run via `make`
-            // at the top level
-            .env_remove("MAKEFLAGS")
-            .env_remove("MFLAGS")
-            .env_remove("CARGO_MAKEFLAGS");
+            .env("LLVM_COMPONENTS", &self.config.llvm_components);
 
         if let Some(ref rustdoc) = self.config.rustdoc_path {
             cmd.env("RUSTDOC", cwd.join(rustdoc));
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 09a7f0395cf..ec20bda8c18 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -1,7 +1,7 @@
 use crate::common::Config;
 use std::env;
 use std::ffi::OsStr;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process::Command;
 
 use tracing::*;
@@ -76,3 +76,17 @@ pub fn add_dylib_path(cmd: &mut Command, paths: impl Iterator<Item = impl Into<P
     let new_paths = paths.map(Into::into).chain(old_paths.into_iter().flatten());
     cmd.env(dylib_env_var(), env::join_paths(new_paths).unwrap());
 }
+
+pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
+    std::fs::create_dir_all(&dst)?;
+    for entry in std::fs::read_dir(src)? {
+        let entry = entry?;
+        let ty = entry.file_type()?;
+        if ty.is_dir() {
+            copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
+        } else {
+            std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
+        }
+    }
+    Ok(())
+}
diff --git a/src/tools/run-make-support/src/cc.rs b/src/tools/run-make-support/src/cc.rs
index 1472a39305e..b33004974bf 100644
--- a/src/tools/run-make-support/src/cc.rs
+++ b/src/tools/run-make-support/src/cc.rs
@@ -1,9 +1,7 @@
 use std::path::Path;
 use std::process::Command;
 
-use crate::{
-    bin_name, cygpath_windows, env_var, handle_failed_output, is_msvc, is_windows, tmp_dir, uname,
-};
+use crate::{bin_name, cygpath_windows, env_var, handle_failed_output, is_msvc, is_windows, uname};
 
 /// Construct a new platform-specific C compiler invocation.
 ///
@@ -54,8 +52,7 @@ impl Cc {
         self
     }
 
-    /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler. This assumes that the executable
-    /// is under `$TMPDIR`.
+    /// Specify `-o` or `-Fe`/`-Fo` depending on platform/compiler.
     pub fn out_exe(&mut self, name: &str) -> &mut Self {
         // Ref: tools.mk (irrelevant lines omitted):
         //
@@ -69,13 +66,13 @@ impl Cc {
         // ```
 
         if is_msvc() {
-            let fe_path = cygpath_windows(tmp_dir().join(bin_name(name)));
-            let fo_path = cygpath_windows(tmp_dir().join(format!("{name}.obj")));
+            let fe_path = cygpath_windows(bin_name(name));
+            let fo_path = cygpath_windows(format!("{name}.obj"));
             self.cmd.arg(format!("-Fe:{fe_path}"));
             self.cmd.arg(format!("-Fo:{fo_path}"));
         } else {
             self.cmd.arg("-o");
-            self.cmd.arg(tmp_dir().join(name));
+            self.cmd.arg(name);
         }
 
         self
diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs
index 63c5af17c1d..7d9246b5222 100644
--- a/src/tools/run-make-support/src/clang.rs
+++ b/src/tools/run-make-support/src/clang.rs
@@ -1,7 +1,7 @@
 use std::path::Path;
 use std::process::Command;
 
-use crate::{bin_name, env_var, handle_failed_output, tmp_dir};
+use crate::{bin_name, env_var, handle_failed_output};
 
 /// Construct a new `clang` invocation. `clang` is not always available for all targets.
 pub fn clang() -> Clang {
@@ -30,11 +30,11 @@ impl Clang {
         self
     }
 
-    /// Specify the name of the executable. The executable will be placed under `$TMPDIR`, and the
-    /// extension will be determined by [`bin_name`].
+    /// Specify the name of the executable. The executable will be placed under the current directory
+    /// and the extension will be determined by [`bin_name`].
     pub fn out_exe(&mut self, name: &str) -> &mut Self {
         self.cmd.arg("-o");
-        self.cmd.arg(tmp_dir().join(bin_name(name)));
+        self.cmd.arg(bin_name(name));
         self
     }
 
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index d74a0272a62..0d167960c16 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -45,11 +45,6 @@ pub fn env_var_os(name: &str) -> OsString {
     }
 }
 
-/// Path of `TMPDIR` (a temporary build directory, not under `/tmp`).
-pub fn tmp_dir() -> PathBuf {
-    env_var_os("TMPDIR").into()
-}
-
 /// `TARGET`
 pub fn target() -> String {
     env_var("TARGET")
@@ -70,12 +65,6 @@ pub fn is_darwin() -> bool {
     target().contains("darwin")
 }
 
-/// Construct a path to a static library under `$TMPDIR` given the library name. This will return a
-/// path with `$TMPDIR` joined with platform-and-compiler-specific library name.
-pub fn static_lib(name: &str) -> PathBuf {
-    tmp_dir().join(static_lib_name(name))
-}
-
 pub fn python_command() -> Command {
     let python_path = env_var("PYTHON");
     Command::new(python_path)
@@ -89,7 +78,7 @@ pub fn htmldocck() -> Command {
 
 /// Path to the root rust-lang/rust source checkout.
 pub fn source_root() -> PathBuf {
-    env_var("S").into()
+    env_var("SOURCE_ROOT").into()
 }
 
 /// Construct the static library name based on the platform.
@@ -116,12 +105,6 @@ pub fn static_lib_name(name: &str) -> String {
     if is_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") }
 }
 
-/// Construct a path to a dynamic library under `$TMPDIR` given the library name. This will return a
-/// path with `$TMPDIR` joined with platform-and-compiler-specific library name.
-pub fn dynamic_lib(name: &str) -> PathBuf {
-    tmp_dir().join(dynamic_lib_name(name))
-}
-
 /// Construct the dynamic library name based on the platform.
 pub fn dynamic_lib_name(name: &str) -> String {
     // See tools.mk (irrelevant lines omitted):
@@ -159,14 +142,7 @@ pub fn dynamic_lib_extension() -> &'static str {
     }
 }
 
-/// Construct a path to a rust library (rlib) under `$TMPDIR` given the library name. This will return a
-/// path with `$TMPDIR` joined with the library name.
-pub fn rust_lib(name: &str) -> PathBuf {
-    tmp_dir().join(rust_lib_name(name))
-}
-
-/// Generate the name a rust library (rlib) would have. If you want the complete path, use
-/// [`rust_lib`] instead.
+/// Construct a rust library (rlib) name.
 pub fn rust_lib_name(name: &str) -> String {
     format!("lib{name}.rlib")
 }
@@ -176,6 +152,11 @@ pub fn bin_name(name: &str) -> String {
     if is_windows() { format!("{name}.exe") } else { name.to_string() }
 }
 
+/// Return the current working directory.
+pub fn cwd() -> PathBuf {
+    env::current_dir().unwrap()
+}
+
 /// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is
 /// available on the platform!
 #[track_caller]
@@ -227,7 +208,7 @@ pub fn set_host_rpath(cmd: &mut Command) {
     let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR");
     cmd.env(&ld_lib_path_envvar, {
         let mut paths = vec![];
-        paths.push(PathBuf::from(env_var("TMPDIR")));
+        paths.push(cwd());
         paths.push(PathBuf::from(env_var("HOST_RPATH_DIR")));
         for p in env::split_paths(&env_var(&ld_lib_path_envvar)) {
             paths.push(p.to_path_buf());
@@ -315,6 +296,27 @@ pub fn assert_not_contains(haystack: &str, needle: &str) {
     }
 }
 
+/// This function is designed for running commands in a temporary directory
+/// that is cleared after the function ends.
+///
+/// What this function does:
+/// 1) Creates a temporary directory (`tmpdir`)
+/// 2) Copies all files from the current directory to `tmpdir`
+/// 3) Changes the current working directory to `tmpdir`
+/// 4) Calls `callback`
+/// 5) Switches working directory back to the original one
+/// 6) Removes `tmpdir`
+pub fn run_in_tmpdir<F: FnOnce()>(callback: F) {
+    let original_dir = cwd();
+    let tmpdir = original_dir.join("../temporary-directory");
+    copy_dir_all(".", &tmpdir);
+
+    env::set_current_dir(&tmpdir).unwrap();
+    callback();
+    env::set_current_dir(original_dir).unwrap();
+    fs::remove_dir_all(tmpdir).unwrap();
+}
+
 /// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct
 /// containing a `cmd: Command` field and a `output` function. The provided helpers are:
 ///
diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs
index 8d8eafba99b..b607c583e32 100644
--- a/src/tools/run-make-support/src/run.rs
+++ b/src/tools/run-make-support/src/run.rs
@@ -2,19 +2,19 @@ use std::env;
 use std::path::{Path, PathBuf};
 use std::process::{Command, Output};
 
-use crate::{env_var, is_windows};
+use crate::{cwd, env_var, is_windows};
 
 use super::handle_failed_output;
 
 fn run_common(name: &str) -> (Command, Output) {
     let mut bin_path = PathBuf::new();
-    bin_path.push(env_var("TMPDIR"));
+    bin_path.push(cwd());
     bin_path.push(name);
     let ld_lib_path_envvar = env_var("LD_LIB_PATH_ENVVAR");
     let mut cmd = Command::new(bin_path);
     cmd.env(&ld_lib_path_envvar, {
         let mut paths = vec![];
-        paths.push(PathBuf::from(env_var("TMPDIR")));
+        paths.push(cwd());
         for p in env::split_paths(&env_var("TARGET_RPATH_ENV")) {
             paths.push(p.to_path_buf());
         }
diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs
index e923c3cf4ad..a64dd9d30cf 100644
--- a/src/tools/run-make-support/src/rustc.rs
+++ b/src/tools/run-make-support/src/rustc.rs
@@ -3,7 +3,7 @@ use std::io::Write;
 use std::path::Path;
 use std::process::{Command, Output, Stdio};
 
-use crate::{env_var, handle_failed_output, set_host_rpath, tmp_dir};
+use crate::{cwd, env_var, handle_failed_output, set_host_rpath};
 
 /// Construct a new `rustc` invocation.
 pub fn rustc() -> Rustc {
@@ -28,7 +28,7 @@ fn setup_common() -> Command {
     let rustc = env_var("RUSTC");
     let mut cmd = Command::new(rustc);
     set_host_rpath(&mut cmd);
-    cmd.arg("--out-dir").arg(tmp_dir()).arg("-L").arg(tmp_dir());
+    cmd.arg("-L").arg(cwd());
     cmd
 }
 
diff --git a/src/tools/rust-installer/src/compression.rs b/src/tools/rust-installer/src/compression.rs
index 4e840dbfbb4..40c7c597e9b 100644
--- a/src/tools/rust-installer/src/compression.rs
+++ b/src/tools/rust-installer/src/compression.rs
@@ -214,22 +214,16 @@ impl Write for CombinedEncoder {
     }
 
     fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
-        self.encoders
-            .par_iter_mut()
-            .map(|w| w.write_all(buf))
-            .collect::<std::io::Result<Vec<()>>>()?;
-        Ok(())
+        self.encoders.par_iter_mut().try_for_each(|w| w.write_all(buf))
     }
 
     fn flush(&mut self) -> std::io::Result<()> {
-        self.encoders.par_iter_mut().map(|w| w.flush()).collect::<std::io::Result<Vec<()>>>()?;
-        Ok(())
+        self.encoders.par_iter_mut().try_for_each(Write::flush)
     }
 }
 
 impl Encoder for CombinedEncoder {
     fn finish(self: Box<Self>) -> Result<(), Error> {
-        self.encoders.into_par_iter().map(|e| e.finish()).collect::<Result<Vec<()>, Error>>()?;
-        Ok(())
+        self.encoders.into_par_iter().try_for_each(Encoder::finish)
     }
 }
diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js
index f4aadc07199..3eccbc74cb9 100644
--- a/src/tools/rustdoc-gui/.eslintrc.js
+++ b/src/tools/rustdoc-gui/.eslintrc.js
@@ -6,7 +6,7 @@ module.exports = {
     },
     "extends": "eslint:recommended",
     "parserOptions": {
-        "ecmaVersion": 2018,
+        "ecmaVersion": 2019,
         "sourceType": "module"
     },
     "rules": {
diff --git a/src/tools/rustdoc-js/.eslintrc.js b/src/tools/rustdoc-js/.eslintrc.js
index b9d0e251c24..3eccbc74cb9 100644
--- a/src/tools/rustdoc-js/.eslintrc.js
+++ b/src/tools/rustdoc-js/.eslintrc.js
@@ -6,7 +6,7 @@ module.exports = {
     },
     "extends": "eslint:recommended",
     "parserOptions": {
-        "ecmaVersion": 8,
+        "ecmaVersion": 2019,
         "sourceType": "module"
     },
     "rules": {
diff --git a/src/tools/rustfmt/tests/source/attrib.rs b/src/tools/rustfmt/tests/source/attrib.rs
index fc13cd02b03..d45fba55224 100644
--- a/src/tools/rustfmt/tests/source/attrib.rs
+++ b/src/tools/rustfmt/tests/source/attrib.rs
@@ -214,8 +214,8 @@ type Os = NoSource;
 // #3313
 fn stmt_expr_attributes() {
     let foo ;
-    (#[must_use]
-   foo) = false ;
+    #[must_use]
+   foo = false ;
 }
 
 // #3509
diff --git a/src/tools/rustfmt/tests/target/attrib.rs b/src/tools/rustfmt/tests/target/attrib.rs
index 7b3309676de..7e61f68d76a 100644
--- a/src/tools/rustfmt/tests/target/attrib.rs
+++ b/src/tools/rustfmt/tests/target/attrib.rs
@@ -248,8 +248,8 @@ type Os = NoSource;
 // #3313
 fn stmt_expr_attributes() {
     let foo;
-    (#[must_use]
-    foo) = false;
+    #[must_use]
+    foo = false;
 }
 
 // #3509
diff --git a/src/version b/src/version
index aaceec04e04..dbd41264aa9 100644
--- a/src/version
+++ b/src/version
@@ -1 +1 @@
-1.80.0
+1.81.0
diff --git a/tests/codegen-units/item-collection/generic-impl.rs b/tests/codegen-units/item-collection/generic-impl.rs
index 23d09e0d8af..b4cd99272b1 100644
--- a/tests/codegen-units/item-collection/generic-impl.rs
+++ b/tests/codegen-units/item-collection/generic-impl.rs
@@ -22,16 +22,16 @@ impl<T> Struct<T> {
     }
 }
 
-pub struct LifeTimeOnly<'a> {
+pub struct _LifeTimeOnly<'a> {
     _a: &'a u32,
 }
 
-impl<'a> LifeTimeOnly<'a> {
-    //~ MONO_ITEM fn LifeTimeOnly::<'_>::foo
+impl<'a> _LifeTimeOnly<'a> {
+    //~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo
     pub fn foo(&self) {}
-    //~ MONO_ITEM fn LifeTimeOnly::<'_>::bar
+    //~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar
     pub fn bar(&'a self) {}
-    //~ MONO_ITEM fn LifeTimeOnly::<'_>::baz
+    //~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz
     pub fn baz<'b>(&'b self) {}
 
     pub fn non_instantiated<T>(&self) {}
diff --git a/tests/codegen-units/item-collection/overloaded-operators.rs b/tests/codegen-units/item-collection/overloaded-operators.rs
index 69b55695d3d..e00e22dbab9 100644
--- a/tests/codegen-units/item-collection/overloaded-operators.rs
+++ b/tests/codegen-units/item-collection/overloaded-operators.rs
@@ -5,44 +5,44 @@
 
 use std::ops::{Add, Deref, Index, IndexMut};
 
-pub struct Indexable {
+pub struct _Indexable {
     data: [u8; 3],
 }
 
-impl Index<usize> for Indexable {
+impl Index<usize> for _Indexable {
     type Output = u8;
 
-    //~ MONO_ITEM fn <Indexable as std::ops::Index<usize>>::index
+    //~ MONO_ITEM fn <_Indexable as std::ops::Index<usize>>::index
     fn index(&self, index: usize) -> &Self::Output {
         if index >= 3 { &self.data[0] } else { &self.data[index] }
     }
 }
 
-impl IndexMut<usize> for Indexable {
-    //~ MONO_ITEM fn <Indexable as std::ops::IndexMut<usize>>::index_mut
+impl IndexMut<usize> for _Indexable {
+    //~ MONO_ITEM fn <_Indexable as std::ops::IndexMut<usize>>::index_mut
     fn index_mut(&mut self, index: usize) -> &mut Self::Output {
         if index >= 3 { &mut self.data[0] } else { &mut self.data[index] }
     }
 }
 
-//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::eq
-//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::ne
+//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq
+//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne
 #[derive(PartialEq)]
-pub struct Equatable(u32);
+pub struct _Equatable(u32);
 
-impl Add<u32> for Equatable {
+impl Add<u32> for _Equatable {
     type Output = u32;
 
-    //~ MONO_ITEM fn <Equatable as std::ops::Add<u32>>::add
+    //~ MONO_ITEM fn <_Equatable as std::ops::Add<u32>>::add
     fn add(self, rhs: u32) -> u32 {
         self.0 + rhs
     }
 }
 
-impl Deref for Equatable {
+impl Deref for _Equatable {
     type Target = u32;
 
-    //~ MONO_ITEM fn <Equatable as std::ops::Deref>::deref
+    //~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref
     fn deref(&self) -> &Self::Target {
         &self.0
     }
diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs
index 4e5ad8ad4a9..9870ecc69ba 100644
--- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs
+++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs
@@ -82,9 +82,9 @@ pub fn foo12(_: &Type4, _: &Type4, _: &Type4) {}
 // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"}
 // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"}
 // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"}
-// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEE"}
-// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooES0_E"}
-// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooES0_S0_E"}
+// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"}
+// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"}
+// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"}
 // CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"}
 // CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"}
 // CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"}
diff --git a/tests/mir-opt/building/custom/consts.consts.built.after.mir b/tests/mir-opt/building/custom/consts.consts.built.after.mir
index 7b6964849d4..a011fadcef1 100644
--- a/tests/mir-opt/building/custom/consts.consts.built.after.mir
+++ b/tests/mir-opt/building/custom/consts.consts.built.after.mir
@@ -10,7 +10,7 @@ fn consts() -> () {
 
     bb0: {
         _1 = const 5_u8;
-        _2 = const consts::<C>::{constant#1};
+        _2 = const consts::<C>::{constant#0};
         _3 = const C;
         _4 = const D;
         _5 = consts::<10>;
diff --git a/tests/pretty/ast-stmt-expr-attr.rs b/tests/pretty/ast-stmt-expr-attr.rs
index 899f7173f70..fd7272a1b1f 100644
--- a/tests/pretty/ast-stmt-expr-attr.rs
+++ b/tests/pretty/ast-stmt-expr-attr.rs
@@ -13,17 +13,17 @@ fn syntax() {
     let _ = #[attr] ();
     let _ = #[attr] (#[attr] 0,);
     let _ = #[attr] (#[attr] 0, 0);
-    let _ = (#[attr] 0) + #[attr] 0;
-    let _ = (#[attr] 0) / #[attr] 0;
-    let _ = (#[attr] 0) & #[attr] 0;
-    let _ = (#[attr] 0) % #[attr] 0;
+    let _ = #[attr] 0 + #[attr] 0;
+    let _ = #[attr] 0 / #[attr] 0;
+    let _ = #[attr] 0 & #[attr] 0;
+    let _ = #[attr] 0 % #[attr] 0;
     let _ = #[attr] (0 + 0);
     let _ = #[attr] !0;
     let _ = #[attr] -0;
     let _ = #[attr] false;
     let _ = #[attr] 0;
     let _ = #[attr] 'c';
-    let _ = (#[attr] x) as Y;
+    let _ = #[attr] x as Y;
     let _ = #[attr] (x as Y);
     let _ =
         #[attr] while true {
@@ -88,9 +88,9 @@ fn syntax() {
             let _ = ();
             foo
         };
-    let _ = (#[attr] x) = y;
+    let _ = #[attr] x = y;
     let _ = #[attr] (x = y);
-    let _ = (#[attr] x) += y;
+    let _ = #[attr] x += y;
     let _ = #[attr] (x += y);
     let _ = #[attr] foo.bar;
     let _ = (#[attr] foo).bar;
@@ -98,8 +98,8 @@ fn syntax() {
     let _ = (#[attr] foo).0;
     let _ = #[attr] foo[bar];
     let _ = (#[attr] foo)[bar];
-    let _ = (#[attr] 0)..#[attr] 0;
-    let _ = (#[attr] 0)..;
+    let _ = #[attr] 0..#[attr] 0;
+    let _ = #[attr] 0..;
     let _ = #[attr] (0..0);
     let _ = #[attr] (0..);
     let _ = #[attr] (..0);
diff --git a/tests/pretty/stmt_expr_attributes.rs b/tests/pretty/stmt_expr_attributes.rs
index f9041268211..01a503ce7ee 100644
--- a/tests/pretty/stmt_expr_attributes.rs
+++ b/tests/pretty/stmt_expr_attributes.rs
@@ -147,13 +147,13 @@ fn _11() {
     let _ = #[rustc_dummy] (0);
     let _ = #[rustc_dummy] (0,);
     let _ = #[rustc_dummy] (0, 0);
-    let _ = (#[rustc_dummy] 0) + #[rustc_dummy] 0;
+    let _ = #[rustc_dummy] 0 + #[rustc_dummy] 0;
     let _ = #[rustc_dummy] !0;
     let _ = #[rustc_dummy] -0i32;
     let _ = #[rustc_dummy] false;
     let _ = #[rustc_dummy] 'c';
     let _ = #[rustc_dummy] 0;
-    let _ = (#[rustc_dummy] 0) as usize;
+    let _ = #[rustc_dummy] 0 as usize;
     let _ =
         #[rustc_dummy] while false {
             #![rustc_dummy]
@@ -206,10 +206,15 @@ fn _11() {
             let _ = ();
             ()
         };
-    let const {} = #[rustc_dummy] const {};
+    let const {
+                    #![rustc_dummy]
+                } =
+        #[rustc_dummy] const {
+                #![rustc_dummy]
+            };
     let mut x = 0;
-    let _ = (#[rustc_dummy] x) = 15;
-    let _ = (#[rustc_dummy] x) += 15;
+    let _ = #[rustc_dummy] x = 15;
+    let _ = #[rustc_dummy] x += 15;
     let s = Foo { data: () };
     let _ = #[rustc_dummy] s.data;
     let _ = (#[rustc_dummy] s).data;
@@ -219,8 +224,8 @@ fn _11() {
     let v = vec!(0);
     let _ = #[rustc_dummy] v[0];
     let _ = (#[rustc_dummy] v)[0];
-    let _ = (#[rustc_dummy] 0)..#[rustc_dummy] 0;
-    let _ = (#[rustc_dummy] 0)..;
+    let _ = #[rustc_dummy] 0..#[rustc_dummy] 0;
+    let _ = #[rustc_dummy] 0..;
     let _ = #[rustc_dummy] (0..0);
     let _ = #[rustc_dummy] (0..);
     let _ = #[rustc_dummy] (..0);
diff --git a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs
index 1bdb6347571..b56f8dd7acb 100644
--- a/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs
+++ b/tests/run-make/CURRENT_RUSTC_VERSION/rmake.rs
@@ -5,19 +5,19 @@
 
 use std::path::PathBuf;
 
-use run_make_support::{aux_build, rustc};
+use run_make_support::{aux_build, rustc, source_root};
 
 fn main() {
     aux_build().input("stable.rs").emit("metadata").run();
 
-    let mut stable_path = PathBuf::from(env!("TMPDIR"));
-    stable_path.push("libstable.rmeta");
-
-    let output =
-        rustc().input("main.rs").emit("metadata").extern_("stable", &stable_path).command_output();
+    let output = rustc()
+        .input("main.rs")
+        .emit("metadata")
+        .extern_("stable", "libstable.rmeta")
+        .command_output();
 
     let stderr = String::from_utf8_lossy(&output.stderr);
-    let version = include_str!(concat!(env!("S"), "/src/version"));
+    let version = std::fs::read_to_string(source_root().join("src/version")).unwrap();
     let expected_string = format!("stable since {}", version.trim());
     assert!(stderr.contains(&expected_string));
 }
diff --git a/tests/run-make/arguments-non-c-like-enum/rmake.rs b/tests/run-make/arguments-non-c-like-enum/rmake.rs
index 13230206ca8..88f4d664aa6 100644
--- a/tests/run-make/arguments-non-c-like-enum/rmake.rs
+++ b/tests/run-make/arguments-non-c-like-enum/rmake.rs
@@ -1,14 +1,14 @@
 //! Check that non-trivial `repr(C)` enum in Rust has valid C layout.
 //@ ignore-cross-compile
 
-use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib};
+use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name};
 
 pub fn main() {
     use std::path::Path;
 
     rustc().input("nonclike.rs").crate_type("staticlib").run();
     cc().input("test.c")
-        .input(static_lib("nonclike"))
+        .input(static_lib_name("nonclike"))
         .out_exe("test")
         .args(&extra_c_flags())
         .args(&extra_cxx_flags())
diff --git a/tests/run-make/artifact-incr-cache-no-obj/rmake.rs b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs
index 6613698ae1d..d5bc46dff47 100644
--- a/tests/run-make/artifact-incr-cache-no-obj/rmake.rs
+++ b/tests/run-make/artifact-incr-cache-no-obj/rmake.rs
@@ -5,17 +5,15 @@
 //
 // Fixes: rust-lang/rust#123234
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
-    let inc_dir = tmp_dir();
-
     for _ in 0..=1 {
         rustc()
             .input("lib.rs")
             .crate_type("lib")
             .emit("asm,dep-info,link,mir,llvm-ir,llvm-bc")
-            .incremental(&inc_dir)
+            .incremental("incremental")
             .run();
     }
 }
diff --git a/tests/run-make/artifact-incr-cache/rmake.rs b/tests/run-make/artifact-incr-cache/rmake.rs
index 106f363eb8d..b4b63313cfc 100644
--- a/tests/run-make/artifact-incr-cache/rmake.rs
+++ b/tests/run-make/artifact-incr-cache/rmake.rs
@@ -7,17 +7,15 @@
 // Also see discussion at
 // <https://internals.rust-lang.org/t/interaction-between-incremental-compilation-and-emit/20551>
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
-    let inc_dir = tmp_dir();
-
     for _ in 0..=1 {
         rustc()
             .input("lib.rs")
             .crate_type("lib")
             .emit("obj,asm,dep-info,link,mir,llvm-ir,llvm-bc")
-            .incremental(&inc_dir)
+            .incremental("incremental")
             .run();
     }
 }
diff --git a/tests/run-make/bare-outfile/rmake.rs b/tests/run-make/bare-outfile/rmake.rs
index 82d0fab5073..badf1396b73 100644
--- a/tests/run-make/bare-outfile/rmake.rs
+++ b/tests/run-make/bare-outfile/rmake.rs
@@ -3,13 +3,9 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{run, rustc, tmp_dir};
-use std::env;
-use std::fs;
+use run_make_support::{run, rustc};
 
 fn main() {
-    fs::copy("foo.rs", tmp_dir().join("foo.rs")).unwrap();
-    env::set_current_dir(tmp_dir());
     rustc().output("foo").input("foo.rs").run();
     run("foo");
 }
diff --git a/tests/run-make/box-struct-no-segfault/rmake.rs b/tests/run-make/box-struct-no-segfault/rmake.rs
index 5406f765e6c..1bbefd03541 100644
--- a/tests/run-make/box-struct-no-segfault/rmake.rs
+++ b/tests/run-make/box-struct-no-segfault/rmake.rs
@@ -5,9 +5,9 @@
 // This test checks that this bug does not resurface.
 // See https://github.com/rust-lang/rust/issues/28766
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
     rustc().opt().input("foo.rs").run();
-    rustc().opt().library_search_path(tmp_dir()).input("main.rs").run();
+    rustc().opt().input("main.rs").run();
 }
diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs
index 5c4b6d78649..ec42e88032d 100644
--- a/tests/run-make/c-link-to-rust-dylib/rmake.rs
+++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs
@@ -5,29 +5,23 @@
 
 use std::fs::remove_file;
 
-use run_make_support::{
-    cc, dynamic_lib_extension, is_msvc, read_dir, run, run_fail, rustc, tmp_dir,
-};
+use run_make_support::{cc, cwd, dynamic_lib_extension, is_msvc, read_dir, run, run_fail, rustc};
 
 fn main() {
     rustc().input("foo.rs").run();
 
     if is_msvc() {
-        let lib = tmp_dir().join("foo.dll.lib");
+        let lib = "foo.dll.lib";
 
         cc().input("bar.c").arg(lib).out_exe("bar").run();
     } else {
-        cc().input("bar.c")
-            .arg("-lfoo")
-            .output(tmp_dir().join("bar"))
-            .library_search_path(tmp_dir())
-            .run();
+        cc().input("bar.c").arg("-lfoo").output("bar").library_search_path(cwd()).run();
     }
 
     run("bar");
 
     let expected_extension = dynamic_lib_extension();
-    read_dir(tmp_dir(), |path| {
+    read_dir(cwd(), |path| {
         if path.is_file()
             && path.extension().is_some_and(|ext| ext == expected_extension)
             && path.file_name().and_then(|name| name.to_str()).is_some_and(|name| {
diff --git a/tests/run-make/c-link-to-rust-staticlib/rmake.rs b/tests/run-make/c-link-to-rust-staticlib/rmake.rs
index 63d5eb78c69..ca28944a026 100644
--- a/tests/run-make/c-link-to-rust-staticlib/rmake.rs
+++ b/tests/run-make/c-link-to-rust-staticlib/rmake.rs
@@ -3,13 +3,13 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{cc, extra_c_flags, run, rustc, static_lib};
+use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name};
 use std::fs;
 
 fn main() {
     rustc().input("foo.rs").run();
-    cc().input("bar.c").input(static_lib("foo")).out_exe("bar").args(&extra_c_flags()).run();
+    cc().input("bar.c").input(static_lib_name("foo")).out_exe("bar").args(&extra_c_flags()).run();
     run("bar");
-    fs::remove_file(static_lib("foo"));
+    fs::remove_file(static_lib_name("foo"));
     run("bar");
 }
diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
index 7a450efff94..a01e259bce0 100644
--- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
+++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs
@@ -5,12 +5,12 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{cc, extra_c_flags, run, rustc, static_lib};
+use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name};
 
 fn main() {
     rustc().input("checkrust.rs").run();
     cc().input("test.c")
-        .input(static_lib("checkrust"))
+        .input(static_lib_name("checkrust"))
         .out_exe("test")
         .args(&extra_c_flags())
         .run();
diff --git a/tests/run-make/cdylib/rmake.rs b/tests/run-make/cdylib/rmake.rs
index fcb4f56621f..81166867b79 100644
--- a/tests/run-make/cdylib/rmake.rs
+++ b/tests/run-make/cdylib/rmake.rs
@@ -12,24 +12,20 @@
 
 use std::fs::remove_file;
 
-use run_make_support::{cc, dynamic_lib, is_msvc, run, rustc, tmp_dir};
+use run_make_support::{cc, cwd, dynamic_lib_name, is_msvc, run, rustc};
 
 fn main() {
     rustc().input("bar.rs").run();
     rustc().input("foo.rs").run();
 
     if is_msvc() {
-        cc().input("foo.c").arg(tmp_dir().join("foo.dll.lib")).out_exe("foo").run();
+        cc().input("foo.c").arg("foo.dll.lib").out_exe("foo").run();
     } else {
-        cc().input("foo.c")
-            .arg("-lfoo")
-            .output(tmp_dir().join("foo"))
-            .library_search_path(tmp_dir())
-            .run();
+        cc().input("foo.c").arg("-lfoo").library_search_path(cwd()).output("foo").run();
     }
 
     run("foo");
-    remove_file(dynamic_lib("foo")).unwrap();
+    remove_file(dynamic_lib_name("foo")).unwrap();
 
     rustc().input("foo.rs").arg("-Clto").run();
     run("foo");
diff --git a/tests/run-make/compiler-builtins/Cargo.toml b/tests/run-make/compiler-builtins/Cargo.toml
new file mode 100644
index 00000000000..869210c4a68
--- /dev/null
+++ b/tests/run-make/compiler-builtins/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "scratch"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+path = "lib.rs"
diff --git a/tests/run-make/compiler-builtins/lib.rs b/tests/run-make/compiler-builtins/lib.rs
new file mode 100644
index 00000000000..0c9ac1ac8e4
--- /dev/null
+++ b/tests/run-make/compiler-builtins/lib.rs
@@ -0,0 +1 @@
+#![no_std]
diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs
index b043b149d65..309d3a04b21 100644
--- a/tests/run-make/compiler-builtins/rmake.rs
+++ b/tests/run-make/compiler-builtins/rmake.rs
@@ -20,29 +20,17 @@ use run_make_support::object::ObjectSection;
 use run_make_support::object::ObjectSymbol;
 use run_make_support::object::RelocationTarget;
 use run_make_support::set_host_rpath;
-use run_make_support::tmp_dir;
 use run_make_support::{env_var, object};
 use std::collections::HashSet;
-
-const MANIFEST: &str = r#"
-[package]
-name = "scratch"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-path = "lib.rs""#;
+use std::path::PathBuf;
 
 fn main() {
-    let target_dir = tmp_dir().join("target");
+    let target_dir = PathBuf::from("target");
     let target = env_var("TARGET");
 
     println!("Testing compiler_builtins for {}", target);
 
-    // Set up the tiniest Cargo project: An empty no_std library. Just enough to run -Zbuild-std.
-    let manifest_path = tmp_dir().join("Cargo.toml");
-    std::fs::write(&manifest_path, MANIFEST.as_bytes()).unwrap();
-    std::fs::write(tmp_dir().join("lib.rs"), b"#![no_std]").unwrap();
+    let manifest_path = PathBuf::from("Cargo.toml");
 
     let path = env_var("PATH");
     let rustc = env_var("RUSTC");
diff --git a/tests/run-make/const-prop-lint/rmake.rs b/tests/run-make/const-prop-lint/rmake.rs
index fa27a18a591..6d0069a84d7 100644
--- a/tests/run-make/const-prop-lint/rmake.rs
+++ b/tests/run-make/const-prop-lint/rmake.rs
@@ -2,12 +2,12 @@
 
 use std::fs;
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::{cwd, rustc};
 
 fn main() {
     rustc().input("input.rs").run_fail_assert_exit_code(1);
 
-    for entry in fs::read_dir(tmp_dir()).unwrap() {
+    for entry in fs::read_dir(cwd()).unwrap() {
         let entry = entry.unwrap();
         let path = entry.path();
 
diff --git a/tests/run-make/core-no-oom-handling/rmake.rs b/tests/run-make/core-no-oom-handling/rmake.rs
index 3ebbf63ab7d..a9e2b33e210 100644
--- a/tests/run-make/core-no-oom-handling/rmake.rs
+++ b/tests/run-make/core-no-oom-handling/rmake.rs
@@ -2,7 +2,7 @@
 // when the no_global_oom_handling feature is turned on.
 // See https://github.com/rust-lang/rust/pull/110649
 
-use run_make_support::{rustc, source_root, tmp_dir};
+use run_make_support::{rustc, source_root};
 
 fn main() {
     rustc()
@@ -10,7 +10,7 @@ fn main() {
         .arg("-Dwarnings")
         .crate_type("rlib")
         .input(source_root().join("library/core/src/lib.rs"))
-        .sysroot(tmp_dir().join("fakeroot"))
+        .sysroot("fakeroot")
         .cfg("no_global_oom_handling")
         .run();
 }
diff --git a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs
index 61f32762d8b..04afc92bf7e 100644
--- a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs
+++ b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs
@@ -3,7 +3,7 @@
 //@ needs-matching-clang
 //@ needs-llvm-components riscv
 
-use run_make_support::{bin_name, clang, llvm_readobj, rustc, tmp_dir};
+use run_make_support::{bin_name, clang, llvm_readobj, rustc};
 use std::{
     env,
     path::PathBuf,
@@ -30,11 +30,11 @@ fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float:
         .no_stdlib()
         .out_exe("riscv-xlto")
         .input("cstart.c")
-        .input(tmp_dir().join("libriscv_xlto.rlib"))
+        .input("libriscv_xlto.rlib")
         .run();
 
     // Check that the built binary has correct float abi
-    let executable = tmp_dir().join(bin_name("riscv-xlto"));
+    let executable = bin_name("riscv-xlto");
     let output = llvm_readobj().input(&executable).file_header().run();
     let stdout = String::from_utf8_lossy(&output.stdout);
     eprintln!("obj:\n{}", stdout);
diff --git a/tests/run-make/deref-impl-rustdoc-ice/rmake.rs b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs
index c2156de03a9..91fc0a9025f 100644
--- a/tests/run-make/deref-impl-rustdoc-ice/rmake.rs
+++ b/tests/run-make/deref-impl-rustdoc-ice/rmake.rs
@@ -7,10 +7,10 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{rustc, rustdoc, tmp_dir};
+use run_make_support::{cwd, rustc, rustdoc};
 
 fn main() {
     rustc().input("foo.rs").run();
     rustc().input("bar.rs").run();
-    rustdoc().input("baz.rs").library_search_path(tmp_dir()).output(tmp_dir()).run();
+    rustdoc().input("baz.rs").library_search_path(cwd()).output(cwd()).run();
 }
diff --git a/tests/run-make/doctests-keep-binaries/rmake.rs b/tests/run-make/doctests-keep-binaries/rmake.rs
index 0613ef4839b..5bc9480e4a3 100644
--- a/tests/run-make/doctests-keep-binaries/rmake.rs
+++ b/tests/run-make/doctests-keep-binaries/rmake.rs
@@ -1,15 +1,15 @@
 // Check that valid binaries are persisted by running them, regardless of whether the
 // --run or --no-run option is used.
 
-use run_make_support::{run, rustc, rustdoc, tmp_dir};
+use run_make_support::{run, rustc, rustdoc};
 use std::fs::{create_dir, remove_dir_all};
 use std::path::Path;
 
 fn setup_test_env<F: FnOnce(&Path, &Path)>(callback: F) {
-    let out_dir = tmp_dir().join("doctests");
+    let out_dir = Path::new("doctests");
     create_dir(&out_dir).expect("failed to create doctests folder");
     rustc().input("t.rs").crate_type("rlib").run();
-    callback(&out_dir, &tmp_dir().join("libt.rlib"));
+    callback(&out_dir, Path::new("libt.rlib"));
     remove_dir_all(out_dir);
 }
 
@@ -44,19 +44,17 @@ fn main() {
     });
     // Behavior with --test-run-directory with relative paths.
     setup_test_env(|_out_dir, extern_path| {
-        let run_dir = "rundir";
-        let run_dir_path = tmp_dir().join("rundir");
+        let run_dir_path = Path::new("rundir");
         create_dir(&run_dir_path).expect("failed to create rundir folder");
 
         rustdoc()
-            .current_dir(tmp_dir())
-            .input(std::env::current_dir().unwrap().join("t.rs"))
+            .input("t.rs")
             .arg("-Zunstable-options")
             .arg("--test")
             .arg("--persist-doctests")
             .arg("doctests")
             .arg("--test-run-directory")
-            .arg(run_dir)
+            .arg(run_dir_path)
             .extern_("t", "libt.rlib")
             .run();
 
diff --git a/tests/run-make/doctests-runtool/rmake.rs b/tests/run-make/doctests-runtool/rmake.rs
index 6cc7c6bbdaf..6a7a931249e 100644
--- a/tests/run-make/doctests-runtool/rmake.rs
+++ b/tests/run-make/doctests-runtool/rmake.rs
@@ -1,12 +1,11 @@
 // Tests behavior of rustdoc `--runtool`.
 
-use run_make_support::{rustc, rustdoc, tmp_dir};
-use std::env::current_dir;
+use run_make_support::{rustc, rustdoc};
 use std::fs::{create_dir, remove_dir_all};
 use std::path::PathBuf;
 
 fn mkdir(name: &str) -> PathBuf {
-    let dir = tmp_dir().join(name);
+    let dir = PathBuf::from(name);
     create_dir(&dir).expect("failed to create doctests folder");
     dir
 }
@@ -22,7 +21,7 @@ fn main() {
     rustc().input("runtool.rs").output(&run_tool_binary).run();
 
     rustdoc()
-        .input(current_dir().unwrap().join("t.rs"))
+        .input("t.rs")
         .arg("-Zunstable-options")
         .arg("--test")
         .arg("--test-run-directory")
@@ -30,7 +29,6 @@ fn main() {
         .arg("--runtool")
         .arg(&run_tool_binary)
         .extern_("t", "libt.rlib")
-        .current_dir(tmp_dir())
         .run();
 
     remove_dir_all(run_dir);
diff --git a/tests/run-make/emit-named-files/rmake.rs b/tests/run-make/emit-named-files/rmake.rs
index 068f9796d0e..c4b7b9aebf6 100644
--- a/tests/run-make/emit-named-files/rmake.rs
+++ b/tests/run-make/emit-named-files/rmake.rs
@@ -1,7 +1,7 @@
 use std::fs::create_dir;
 use std::path::Path;
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn emit_and_check(out_dir: &Path, out_file: &str, format: &str) {
     let out_file = out_dir.join(out_file);
@@ -10,7 +10,7 @@ fn emit_and_check(out_dir: &Path, out_file: &str, format: &str) {
 }
 
 fn main() {
-    let out_dir = tmp_dir().join("emit");
+    let out_dir = Path::new("emit");
 
     create_dir(&out_dir).unwrap();
 
diff --git a/tests/run-make/exit-code/rmake.rs b/tests/run-make/exit-code/rmake.rs
index 76d7777581b..6bf7a232642 100644
--- a/tests/run-make/exit-code/rmake.rs
+++ b/tests/run-make/exit-code/rmake.rs
@@ -1,6 +1,6 @@
 // Test that we exit with the correct exit code for successful / unsuccessful / ICE compilations
 
-use run_make_support::{rustc, rustdoc, tmp_dir};
+use run_make_support::{rustc, rustdoc};
 
 fn main() {
     rustc().arg("success.rs").run();
@@ -15,7 +15,7 @@ fn main() {
         .arg("compile-error.rs")
         .run_fail_assert_exit_code(101);
 
-    rustdoc().arg("success.rs").output(tmp_dir().join("exit-code")).run();
+    rustdoc().arg("success.rs").output("exit-code").run();
 
     rustdoc().arg("--invalid-arg-foo").run_fail_assert_exit_code(1);
 
diff --git a/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs b/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs
index de4023282ef..e54ef53e3dc 100644
--- a/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs
+++ b/tests/run-make/external-crate-panic-handle-no-lint/rmake.rs
@@ -4,9 +4,9 @@
 // and cause the test to fail.
 // See https://github.com/rust-lang/rust/issues/53964
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
     rustc().input("panic.rs").run();
-    rustc().input("app.rs").panic("abort").emit("obj").library_search_path(tmp_dir()).run();
+    rustc().input("app.rs").panic("abort").emit("obj").run();
 }
diff --git a/tests/run-make/incr-prev-body-beyond-eof/rmake.rs b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs
index 8aa3893727f..ccb1f95275e 100644
--- a/tests/run-make/incr-prev-body-beyond-eof/rmake.rs
+++ b/tests/run-make/incr-prev-body-beyond-eof/rmake.rs
@@ -13,15 +13,15 @@
 //@ ignore-nvptx64-nvidia-cuda
 // FIXME: can't find crate for `std`
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 use std::fs;
 
 fn main() {
     // FIXME(Oneirical): Use run_make_support::fs_wrapper here.
-    fs::create_dir(tmp_dir().join("src")).unwrap();
-    fs::create_dir(tmp_dir().join("incr")).unwrap();
-    fs::copy("a.rs", tmp_dir().join("src/main.rs")).unwrap();
-    rustc().incremental(tmp_dir().join("incr")).input(tmp_dir().join("src/main.rs")).run();
-    fs::copy("b.rs", tmp_dir().join("src/main.rs")).unwrap();
-    rustc().incremental(tmp_dir().join("incr")).input(tmp_dir().join("src/main.rs")).run();
+    fs::create_dir("src").unwrap();
+    fs::create_dir("incr").unwrap();
+    fs::copy("a.rs", "src/main.rs").unwrap();
+    rustc().incremental("incr").input("src/main.rs").run();
+    fs::copy("b.rs", "src/main.rs").unwrap();
+    rustc().incremental("incr").input("src/main.rs").run();
 }
diff --git a/tests/run-make/issue-107495-archive-permissions/rmake.rs b/tests/run-make/issue-107495-archive-permissions/rmake.rs
index db25e9b033c..72ceb10c591 100644
--- a/tests/run-make/issue-107495-archive-permissions/rmake.rs
+++ b/tests/run-make/issue-107495-archive-permissions/rmake.rs
@@ -3,7 +3,7 @@
 #[cfg(unix)]
 extern crate libc;
 
-use run_make_support::{aux_build, tmp_dir};
+use run_make_support::aux_build;
 use std::fs;
 #[cfg(unix)]
 use std::os::unix::fs::PermissionsExt;
@@ -16,7 +16,7 @@ fn main() {
     }
 
     aux_build().arg("foo.rs").run();
-    verify(&tmp_dir().join("libfoo.rlib"));
+    verify(Path::new("libfoo.rlib"));
 }
 
 fn verify(path: &Path) {
diff --git a/tests/run-make/issue-125484-used-dependencies/rmake.rs b/tests/run-make/issue-125484-used-dependencies/rmake.rs
index b75e82b42db..bc0a18de66e 100644
--- a/tests/run-make/issue-125484-used-dependencies/rmake.rs
+++ b/tests/run-make/issue-125484-used-dependencies/rmake.rs
@@ -6,7 +6,7 @@
 // make compiletest annotations reproduce the ICE with the minimizations from issues #125474 and
 // #125484.
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
     // The dependency is not itself significant, apart from sharing a name with one of main's
@@ -14,5 +14,5 @@ fn main() {
     rustc().crate_name("same").crate_type("rlib").input("dependency.rs").run();
 
     // Here, an ICE would happen when building the linker command.
-    rustc().input("main.rs").extern_("same", tmp_dir().join("libsame.rlib")).run();
+    rustc().input("main.rs").extern_("same", "libsame.rlib").run();
 }
diff --git a/tests/run-make/manual-crate-name/rmake.rs b/tests/run-make/manual-crate-name/rmake.rs
index 531f531abd2..f171f78087c 100644
--- a/tests/run-make/manual-crate-name/rmake.rs
+++ b/tests/run-make/manual-crate-name/rmake.rs
@@ -1,6 +1,7 @@
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
+use std::path::Path;
 
 fn main() {
     rustc().input("bar.rs").crate_name("foo").run();
-    assert!(tmp_dir().join("libfoo.rlib").is_file());
+    assert!(Path::new("libfoo.rlib").is_file());
 }
diff --git a/tests/run-make/mixing-formats/rmake.rs b/tests/run-make/mixing-formats/rmake.rs
index 0d40b0325f7..444751419d7 100644
--- a/tests/run-make/mixing-formats/rmake.rs
+++ b/tests/run-make/mixing-formats/rmake.rs
@@ -12,31 +12,24 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::{run_in_tmpdir, rustc};
 use std::fs;
 
-fn test_with_teardown(rustc_calls: impl Fn()) {
-    rustc_calls();
-    //FIXME(Oneirical): This should be replaced with the run-make-support fs wrappers.
-    fs::remove_dir_all(tmp_dir()).unwrap();
-    fs::create_dir(tmp_dir()).unwrap();
-}
-
 fn main() {
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         // Building just baz
         rustc().crate_type("rlib").input("foo.rs").run();
         rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("dylib,rlib").input("baz.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("bin").input("baz.rs").run();
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("rlib").input("bar1.rs").run();
         rustc().crate_type("dylib,rlib").input("baz.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("bin").input("baz.rs").run();
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         // Building baz2
         rustc().crate_type("rlib").input("foo.rs").run();
         rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run();
@@ -44,42 +37,42 @@ fn main() {
         rustc().crate_type("dylib").input("baz2.rs").run_fail_assert_exit_code(1);
         rustc().crate_type("bin").input("baz2.rs").run_fail_assert_exit_code(1);
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         rustc().crate_type("rlib").input("foo.rs").run();
         rustc().crate_type("rlib").input("bar1.rs").run();
         rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("dylib,rlib").input("baz2.rs").run();
         rustc().crate_type("bin").input("baz2.rs").run();
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         rustc().crate_type("rlib").input("foo.rs").run();
         rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("rlib").input("bar2.rs").run();
         rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("bin").input("baz2.rs").run();
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         rustc().crate_type("rlib").input("foo.rs").run();
         rustc().crate_type("rlib").input("bar1.rs").run();
         rustc().crate_type("rlib").input("bar2.rs").run();
         rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("bin").input("baz2.rs").run();
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("rlib").input("bar1.rs").run();
         rustc().crate_type("rlib").input("bar2.rs").run();
         rustc().crate_type("dylib,rlib").input("baz2.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("bin").input("baz2.rs").run();
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("dylib").input("bar1.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("rlib").input("bar2.rs").run();
         rustc().crate_type("dylib,rlib").input("baz2.rs").run();
         rustc().crate_type("bin").input("baz2.rs").run();
     });
-    test_with_teardown(|| {
+    run_in_tmpdir(|| {
         rustc().crate_type("dylib").input("foo.rs").arg("-Cprefer-dynamic").run();
         rustc().crate_type("rlib").input("bar1.rs").run();
         rustc().crate_type("dylib").input("bar2.rs").arg("-Cprefer-dynamic").run();
diff --git a/tests/run-make/no-intermediate-extras/rmake.rs b/tests/run-make/no-intermediate-extras/rmake.rs
index 19479e3bd5b..0641417504e 100644
--- a/tests/run-make/no-intermediate-extras/rmake.rs
+++ b/tests/run-make/no-intermediate-extras/rmake.rs
@@ -5,13 +5,13 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 use std::fs;
 
 fn main() {
     rustc().crate_type("rlib").arg("--test").input("foo.rs").run();
     assert!(
-        fs::remove_file(tmp_dir().join("foo.bc")).is_err(),
+        fs::remove_file("foo.bc").is_err(),
         "An unwanted .bc file was created by run-make/no-intermediate-extras."
     );
 }
diff --git a/tests/run-make/non-pie-thread-local/rmake.rs b/tests/run-make/non-pie-thread-local/rmake.rs
index 1ef447e7860..1758f1a0855 100644
--- a/tests/run-make/non-pie-thread-local/rmake.rs
+++ b/tests/run-make/non-pie-thread-local/rmake.rs
@@ -7,13 +7,13 @@
 //@ ignore-cross-compile
 //@ only-linux
 
-use run_make_support::{cc, run, rustc, tmp_dir};
+use run_make_support::{cc, cwd, run, rustc};
 
 fn main() {
     rustc().input("foo.rs").run();
     cc().input("foo.c")
         .arg("-lfoo")
-        .library_search_path(tmp_dir())
+        .library_search_path(cwd())
         .arg("-Wl,--gc-sections")
         .arg("-lpthread")
         .arg("-ldl")
@@ -22,7 +22,7 @@ fn main() {
     run("foo");
     cc().input("foo.c")
         .arg("-lfoo")
-        .library_search_path(tmp_dir())
+        .library_search_path(cwd())
         .arg("-Wl,--gc-sections")
         .arg("-lpthread")
         .arg("-ldl")
diff --git a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
index 40152e0411d..ba1bd448743 100644
--- a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
+++ b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs
@@ -1,21 +1,21 @@
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
     #[cfg(unix)]
     let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]);
     #[cfg(windows)]
     let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]);
-    match std::fs::create_dir(tmp_dir().join(&non_unicode)) {
+    match std::fs::create_dir(&non_unicode) {
         // If an error occurs, check if creating a directory with a valid Unicode name would
         // succeed.
-        Err(e) if std::fs::create_dir(tmp_dir().join("valid_unicode")).is_ok() => {
+        Err(e) if std::fs::create_dir("valid_unicode").is_ok() => {
             // Filesystem doesn't appear support non-Unicode paths.
             return;
         }
         Err(e) => panic!("error creating non-Unicode directory: {e}"),
         _ => {}
     }
-    let incr_dir = tmp_dir().join("incr-dir");
+    let incr_dir = "incr-dir";
     rustc().input("foo.rs").incremental(&incr_dir).run();
     for crate_dir in std::fs::read_dir(&incr_dir).unwrap() {
         std::fs::create_dir(crate_dir.unwrap().path().join(&non_unicode)).unwrap();
diff --git a/tests/run-make/notify-all-emit-artifacts/rmake.rs b/tests/run-make/notify-all-emit-artifacts/rmake.rs
index c866d9179f9..1c2e08ca8f5 100644
--- a/tests/run-make/notify-all-emit-artifacts/rmake.rs
+++ b/tests/run-make/notify-all-emit-artifacts/rmake.rs
@@ -6,11 +6,9 @@
 // See <https://internals.rust-lang.org/t/easier-access-to-files-generated-by-emit-foo/20477>
 extern crate run_make_support;
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::{cwd, rustc};
 
 fn main() {
-    let inc_dir = tmp_dir();
-
     // With single codegen unit files are renamed to match the source file name
     for _ in 0..=1 {
         let output = rustc()
@@ -19,7 +17,7 @@ fn main() {
             .codegen_units(1)
             .json("artifacts")
             .error_format("json")
-            .incremental(&inc_dir)
+            .incremental(cwd())
             .run();
         let stderr = String::from_utf8_lossy(&output.stderr);
         for file in &["lib.o", "lib.ll", "lib.bc", "lib.s"] {
@@ -35,7 +33,7 @@ fn main() {
             .codegen_units(2)
             .json("artifacts")
             .error_format("json")
-            .incremental(&inc_dir)
+            .incremental(cwd())
             .run();
         let stderr = String::from_utf8_lossy(&output.stderr);
         for file in &["rcgu.o", "rcgu.ll", "rcgu.bc", "rcgu.s"] {
diff --git a/tests/run-make/panic-impl-transitive/rmake.rs b/tests/run-make/panic-impl-transitive/rmake.rs
index 86308f593b3..c09b233b23e 100644
--- a/tests/run-make/panic-impl-transitive/rmake.rs
+++ b/tests/run-make/panic-impl-transitive/rmake.rs
@@ -6,14 +6,9 @@
 // function in the crate.
 // See https://github.com/rust-lang/rust/pull/50338
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
     rustc().input("panic-impl-provider.rs").run();
-    rustc()
-        .input("panic-impl-consumer.rs")
-        .panic("abort")
-        .emit("llvm-ir")
-        .library_search_path(tmp_dir())
-        .run();
+    rustc().input("panic-impl-consumer.rs").panic("abort").emit("llvm-ir").run();
 }
diff --git a/tests/run-make/print-cfg/rmake.rs b/tests/run-make/print-cfg/rmake.rs
index 6e72c16f1f9..29cf0ba1b3f 100644
--- a/tests/run-make/print-cfg/rmake.rs
+++ b/tests/run-make/print-cfg/rmake.rs
@@ -7,10 +7,10 @@
 
 use std::collections::HashSet;
 use std::ffi::OsString;
-use std::io::BufRead;
 use std::iter::FromIterator;
+use std::path::PathBuf;
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 struct PrintCfg {
     target: &'static str,
@@ -91,7 +91,7 @@ fn check(PrintCfg { target, includes, disallow }: PrintCfg) {
 
     // --print=cfg=PATH
     {
-        let tmp_path = tmp_dir().join(format!("{target}.cfg"));
+        let tmp_path = PathBuf::from(format!("{target}.cfg"));
         let mut print_arg = OsString::from("--print=cfg=");
         print_arg.push(tmp_path.as_os_str());
 
diff --git a/tests/run-make/print-to-output/rmake.rs b/tests/run-make/print-to-output/rmake.rs
index 1763cd378d2..b3f77e0633c 100644
--- a/tests/run-make/print-to-output/rmake.rs
+++ b/tests/run-make/print-to-output/rmake.rs
@@ -2,8 +2,9 @@
 //! output to a file (instead of stdout)
 
 use std::ffi::OsString;
+use std::path::PathBuf;
 
-use run_make_support::{rustc, target, tmp_dir};
+use run_make_support::{rustc, target};
 
 struct Option<'a> {
     target: &'a str,
@@ -46,7 +47,7 @@ fn check(args: Option) {
 
     // --print={option}=PATH
     let output = {
-        let tmp_path = tmp_dir().join(format!("{}.txt", args.option));
+        let tmp_path = PathBuf::from(format!("{}.txt", args.option));
         let mut print_arg = OsString::from(format!("--print={}=", args.option));
         print_arg.push(tmp_path.as_os_str());
 
diff --git a/tests/run-make/reachable-extern-fn-available-lto/rmake.rs b/tests/run-make/reachable-extern-fn-available-lto/rmake.rs
index c7262b9461b..558444846fd 100644
--- a/tests/run-make/reachable-extern-fn-available-lto/rmake.rs
+++ b/tests/run-make/reachable-extern-fn-available-lto/rmake.rs
@@ -9,10 +9,10 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{cc, extra_c_flags, run, rustc, static_lib};
+use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name};
 
 fn main() {
-    let libbar_path = static_lib("bar");
+    let libbar_path = static_lib_name("bar");
     rustc().input("foo.rs").crate_type("rlib").run();
     rustc()
         .input("bar.rs")
diff --git a/tests/run-make/repr128-dwarf/rmake.rs b/tests/run-make/repr128-dwarf/rmake.rs
index fd5dd810444..27e32099396 100644
--- a/tests/run-make/repr128-dwarf/rmake.rs
+++ b/tests/run-make/repr128-dwarf/rmake.rs
@@ -1,15 +1,15 @@
 //@ ignore-windows
 // This test should be replaced with one in tests/debuginfo once GDB or LLDB support 128-bit enums.
 
-use gimli::{AttributeValue, Dwarf, EndianRcSlice, Reader, RunTimeEndian};
+use gimli::{AttributeValue, EndianRcSlice, Reader, RunTimeEndian};
 use object::{Object, ObjectSection};
-use run_make_support::{gimli, object, rustc, tmp_dir};
-use std::borrow::Cow;
+use run_make_support::{gimli, object, rustc};
 use std::collections::HashMap;
+use std::path::PathBuf;
 use std::rc::Rc;
 
 fn main() {
-    let output = tmp_dir().join("repr128");
+    let output = PathBuf::from("repr128");
     rustc().input("main.rs").output(&output).arg("-Cdebuginfo=2").run();
     // Mach-O uses packed debug info
     let dsym_location = output
diff --git a/tests/run-make/reset-codegen-1/rmake.rs b/tests/run-make/reset-codegen-1/rmake.rs
index 4b91ba7df90..c1385b2e015 100644
--- a/tests/run-make/reset-codegen-1/rmake.rs
+++ b/tests/run-make/reset-codegen-1/rmake.rs
@@ -7,12 +7,12 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 use std::fs;
 
 fn compile(output_file: &str, emit: Option<&str>) {
     let mut rustc = rustc();
-    let rustc = rustc.codegen_units(4).output(tmp_dir().join(output_file)).input("foo.rs");
+    let rustc = rustc.codegen_units(4).output(output_file).input("foo.rs");
     if let Some(emit) = emit {
         rustc.emit(emit);
     }
diff --git a/tests/run-make/resolve-rename/rmake.rs b/tests/run-make/resolve-rename/rmake.rs
index 8c6e3c24714..a5f48c2bbcc 100644
--- a/tests/run-make/resolve-rename/rmake.rs
+++ b/tests/run-make/resolve-rename/rmake.rs
@@ -5,12 +5,11 @@
 // the renamed library.
 // See https://github.com/rust-lang/rust/pull/49253
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 use std::fs;
 fn main() {
     rustc().extra_filename("-hash").input("foo.rs").run();
     rustc().input("bar.rs").run();
-    fs::rename(tmp_dir().join("libfoo-hash.rlib"), tmp_dir().join("libfoo-another-hash.rlib"))
-        .unwrap();
+    fs::rename("libfoo-hash.rlib", "libfoo-another-hash.rlib").unwrap();
     rustc().input("baz.rs").run();
 }
diff --git a/tests/run-make/rustdoc-determinism/rmake.rs b/tests/run-make/rustdoc-determinism/rmake.rs
index 09097d4507d..2d4f357d681 100644
--- a/tests/run-make/rustdoc-determinism/rmake.rs
+++ b/tests/run-make/rustdoc-determinism/rmake.rs
@@ -1,14 +1,15 @@
 // Assert that the search index is generated deterministically, regardless of the
 // order that crates are documented in.
 
-use run_make_support::{diff, rustdoc, tmp_dir};
+use run_make_support::{diff, rustdoc};
+use std::path::Path;
 
 fn main() {
-    let foo_first = tmp_dir().join("foo_first");
+    let foo_first = Path::new("foo_first");
     rustdoc().input("foo.rs").output(&foo_first).run();
     rustdoc().input("bar.rs").output(&foo_first).run();
 
-    let bar_first = tmp_dir().join("bar_first");
+    let bar_first = Path::new("bar_first");
     rustdoc().input("bar.rs").output(&bar_first).run();
     rustdoc().input("foo.rs").output(&bar_first).run();
 
diff --git a/tests/run-make/rustdoc-map-file/rmake.rs b/tests/run-make/rustdoc-map-file/rmake.rs
index d017b41bcdd..de75561c9fb 100644
--- a/tests/run-make/rustdoc-map-file/rmake.rs
+++ b/tests/run-make/rustdoc-map-file/rmake.rs
@@ -1,7 +1,7 @@
-use run_make_support::{python_command, rustdoc, tmp_dir};
+use run_make_support::{python_command, rustdoc};
 
 fn main() {
-    let out_dir = tmp_dir().join("out");
+    let out_dir = "out";
     rustdoc()
         .input("foo.rs")
         .arg("-Zunstable-options")
diff --git a/tests/run-make/rustdoc-output-path/rmake.rs b/tests/run-make/rustdoc-output-path/rmake.rs
index 74fb0a56ac6..28e0fd98f93 100644
--- a/tests/run-make/rustdoc-output-path/rmake.rs
+++ b/tests/run-make/rustdoc-output-path/rmake.rs
@@ -1,9 +1,10 @@
 // Checks that if the output folder doesn't exist, rustdoc will create it.
 
-use run_make_support::{rustdoc, tmp_dir};
+use run_make_support::rustdoc;
+use std::path::Path;
 
 fn main() {
-    let out_dir = tmp_dir().join("foo/bar/doc");
+    let out_dir = Path::new("foo/bar/doc");
     rustdoc().input("foo.rs").output(&out_dir).run();
     assert!(out_dir.exists());
 }
diff --git a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
index 81b7defafc6..a6d08f28cda 100644
--- a/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
+++ b/tests/run-make/rustdoc-scrape-examples-macros/rmake.rs
@@ -1,11 +1,10 @@
 //@ ignore-cross-compile
 
-use run_make_support::{htmldocck, rustc, rustdoc, tmp_dir};
+use run_make_support::{htmldocck, rust_lib_name, rustc, rustdoc};
 
 fn main() {
-    let tmp_dir = tmp_dir();
-    let out_dir = tmp_dir.join("rustdoc");
-    let ex_dir = tmp_dir.join("ex.calls");
+    let out_dir = "rustdoc";
+    let ex_dir = "ex.calls";
     let proc_crate_name = "foobar_macro";
     let crate_name = "foobar";
 
@@ -41,8 +40,8 @@ fn main() {
         .crate_name("ex")
         .crate_type("bin")
         .output(&out_dir)
-        .extern_(crate_name, tmp_dir.join(format!("lib{crate_name}.rlib")))
-        .extern_(proc_crate_name, tmp_dir.join(dylib_name.trim()))
+        .extern_(crate_name, rust_lib_name(crate_name))
+        .extern_(proc_crate_name, dylib_name.trim())
         .arg("-Zunstable-options")
         .arg("--scrape-examples-output-path")
         .arg(&ex_dir)
diff --git a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
index b372c25447d..41efb837458 100644
--- a/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
+++ b/tests/run-make/rustdoc-scrape-examples-remap/scrape.rs
@@ -1,10 +1,9 @@
-use run_make_support::{htmldocck, rustc, rustdoc, source_root, tmp_dir};
+use run_make_support::{htmldocck, rustc, rustdoc, source_root};
 use std::fs::read_dir;
 use std::path::Path;
 
 pub fn scrape(extra_args: &[&str]) {
-    let lib_dir = tmp_dir();
-    let out_dir = tmp_dir().join("rustdoc");
+    let out_dir = Path::new("rustdoc");
     let crate_name = "foobar";
     let deps = read_dir("examples")
         .unwrap()
@@ -23,7 +22,7 @@ pub fn scrape(extra_args: &[&str]) {
             .crate_name(&dep_stem)
             .crate_type("bin")
             .output(&out_dir)
-            .extern_(crate_name, lib_dir.join(format!("lib{crate_name}.rmeta")))
+            .extern_(crate_name, format!("lib{crate_name}.rmeta"))
             .arg("-Zunstable-options")
             .arg("--scrape-examples-output-path")
             .arg(&out_example)
diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
index 66530a4f08e..3246fc56506 100644
--- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
+++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
@@ -1,14 +1,14 @@
 // Test that rustdoc will properly canonicalize the target spec json path just like rustc.
 
-use run_make_support::{rustc, rustdoc, tmp_dir};
+use run_make_support::{cwd, rustc, rustdoc};
 
 fn main() {
-    let out_dir = tmp_dir().join("rustdoc-target-spec-json-path");
+    let out_dir = "rustdoc-target-spec-json-path";
     rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run();
     rustdoc()
         .input("my_crate.rs")
         .output(out_dir)
-        .library_search_path(tmp_dir())
+        .library_search_path(cwd())
         .target("target.json")
         .run();
 }
diff --git a/tests/run-make/rustdoc-test-args/rmake.rs b/tests/run-make/rustdoc-test-args/rmake.rs
index 66f3f7cf131..340ab022880 100644
--- a/tests/run-make/rustdoc-test-args/rmake.rs
+++ b/tests/run-make/rustdoc-test-args/rmake.rs
@@ -1,4 +1,4 @@
-use run_make_support::{rustdoc, tmp_dir};
+use run_make_support::rustdoc;
 use std::path::Path;
 use std::{fs, iter};
 
@@ -8,8 +8,8 @@ fn generate_a_lot_of_cfgs(path: &Path) {
 }
 
 fn main() {
-    let arg_file = tmp_dir().join("args");
+    let arg_file = Path::new("args");
     generate_a_lot_of_cfgs(&arg_file);
 
-    rustdoc().out_dir(tmp_dir()).input("foo.rs").arg_file(&arg_file).arg("--test").run();
+    rustdoc().input("foo.rs").arg_file(&arg_file).arg("--test").run();
 }
diff --git a/tests/run-make/rustdoc-themes/rmake.rs b/tests/run-make/rustdoc-themes/rmake.rs
index d6ddd45b4a4..c28821b7628 100644
--- a/tests/run-make/rustdoc-themes/rmake.rs
+++ b/tests/run-make/rustdoc-themes/rmake.rs
@@ -1,10 +1,11 @@
 // Test that rustdoc will properly load in a theme file and display it in the theme selector.
 
-use run_make_support::{htmldocck, rustdoc, source_root, tmp_dir};
+use run_make_support::{htmldocck, rustdoc, source_root};
+use std::path::Path;
 
 fn main() {
-    let out_dir = tmp_dir().join("rustdoc-themes");
-    let test_css = out_dir.join("test.css");
+    let out_dir = Path::new("rustdoc-themes");
+    let test_css = "test.css";
 
     let no_script =
         std::fs::read_to_string(source_root().join("src/librustdoc/html/static/css/noscript.css"))
diff --git a/tests/run-make/rustdoc-verify-output-files/rmake.rs b/tests/run-make/rustdoc-verify-output-files/rmake.rs
index 212e7eaba2d..d2d12ae83a2 100644
--- a/tests/run-make/rustdoc-verify-output-files/rmake.rs
+++ b/tests/run-make/rustdoc-verify-output-files/rmake.rs
@@ -1,7 +1,7 @@
 use std::fs::copy;
 use std::path::{Path, PathBuf};
 
-use run_make_support::{copy_dir_all, recursive_diff, rustdoc, tmp_dir};
+use run_make_support::{copy_dir_all, recursive_diff, rustdoc};
 
 #[derive(PartialEq)]
 enum JsonOutput {
@@ -19,8 +19,8 @@ fn generate_docs(out_dir: &Path, json_output: JsonOutput) {
 }
 
 fn main() {
-    let out_dir = tmp_dir().join("rustdoc");
-    let tmp_out_dir = tmp_dir().join("tmp-rustdoc");
+    let out_dir = PathBuf::from("rustdoc");
+    let tmp_out_dir = PathBuf::from("tmp-rustdoc");
 
     // Generate HTML docs.
     generate_docs(&out_dir, JsonOutput::No);
diff --git a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
index 86471093834..405da8412ae 100644
--- a/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-out-dir-option/rmake.rs
@@ -1,7 +1,7 @@
-use run_make_support::{htmldocck, rustdoc, tmp_dir};
+use run_make_support::{htmldocck, rustdoc};
 
 fn main() {
-    let out_dir = tmp_dir().join("rustdoc");
+    let out_dir = "rustdoc";
     rustdoc().input("src/lib.rs").crate_name("foobar").crate_type("lib").output(&out_dir).run();
     assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success());
 }
diff --git a/tests/run-make/rustdoc-with-output-option/rmake.rs b/tests/run-make/rustdoc-with-output-option/rmake.rs
index 1a009419273..a3b1c8ca0dd 100644
--- a/tests/run-make/rustdoc-with-output-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-output-option/rmake.rs
@@ -1,7 +1,7 @@
-use run_make_support::{htmldocck, rustdoc, tmp_dir};
+use run_make_support::{htmldocck, rustdoc};
 
 fn main() {
-    let out_dir = tmp_dir().join("rustdoc");
+    let out_dir = "rustdoc";
 
     rustdoc()
         .input("src/lib.rs")
diff --git a/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs
index 6206173ecf1..b536fbe2303 100644
--- a/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs
+++ b/tests/run-make/rustdoc-with-short-out-dir-option/rmake.rs
@@ -1,7 +1,7 @@
-use run_make_support::{htmldocck, rustdoc, tmp_dir};
+use run_make_support::{htmldocck, rustdoc};
 
 fn main() {
-    let out_dir = tmp_dir().join("rustdoc");
+    let out_dir = "rustdoc";
 
     rustdoc()
         .input("src/lib.rs")
diff --git a/tests/run-make/same-lib-two-locations-no-panic/bar.rs b/tests/run-make/same-lib-two-locations-no-panic/bar.rs
deleted file mode 100644
index bb7b36c496e..00000000000
--- a/tests/run-make/same-lib-two-locations-no-panic/bar.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-extern crate foo;
-
-fn main() {}
diff --git a/tests/run-make/same-lib-two-locations-no-panic/foo.rs b/tests/run-make/same-lib-two-locations-no-panic/foo.rs
deleted file mode 100644
index 82b2dfe9fdb..00000000000
--- a/tests/run-make/same-lib-two-locations-no-panic/foo.rs
+++ /dev/null
@@ -1 +0,0 @@
-#![crate_name = "foo"]
diff --git a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs b/tests/run-make/same-lib-two-locations-no-panic/rmake.rs
deleted file mode 100644
index 2900c3c8b74..00000000000
--- a/tests/run-make/same-lib-two-locations-no-panic/rmake.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-// A path which contains the same rlib or dylib in two locations
-// should not cause an assertion panic in the compiler.
-// This test tries to replicate the linked issue and checks
-// if the bugged error makes a resurgence.
-
-// See https://github.com/rust-lang/rust/issues/11908
-
-//@ ignore-cross-compile
-
-use run_make_support::{dynamic_lib, rust_lib, rustc, tmp_dir};
-use std::fs;
-
-fn main() {
-    let tmp_dir_other = tmp_dir().join("other");
-
-    fs::create_dir(&tmp_dir_other);
-    rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run();
-    fs::rename(dynamic_lib("foo"), &tmp_dir_other);
-    rustc().input("foo.rs").crate_type("dylib").arg("-Cprefer-dynamic").run();
-    rustc().input("bar.rs").library_search_path(&tmp_dir_other).run();
-    fs::remove_dir_all(tmp_dir());
-
-    fs::create_dir_all(&tmp_dir_other);
-    rustc().input("foo.rs").crate_type("rlib").run();
-    fs::rename(rust_lib("foo"), &tmp_dir_other);
-    rustc().input("foo.rs").crate_type("rlib").run();
-    rustc().input("bar.rs").library_search_path(tmp_dir_other).run();
-}
diff --git a/tests/run-make/stdin-rustc/rmake.rs b/tests/run-make/stdin-rustc/rmake.rs
index c07a6df4d84..1869b1dcb8c 100644
--- a/tests/run-make/stdin-rustc/rmake.rs
+++ b/tests/run-make/stdin-rustc/rmake.rs
@@ -1,6 +1,7 @@
 //! This test checks rustc `-` (stdin) support
 
-use run_make_support::{is_windows, rustc, tmp_dir};
+use run_make_support::{is_windows, rustc};
+use std::path::PathBuf;
 
 const HELLO_WORLD: &str = r#"
 fn main() {
@@ -11,12 +12,12 @@ fn main() {
 const NOT_UTF8: &[u8] = &[0xff, 0xff, 0xff];
 
 fn main() {
-    let out_dir = tmp_dir();
-
     // echo $HELLO_WORLD | rustc -
     rustc().arg("-").stdin(HELLO_WORLD).run();
     assert!(
-        out_dir.join(if !is_windows() { "rust_out" } else { "rust_out.exe" }).try_exists().unwrap()
+        PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" })
+            .try_exists()
+            .unwrap()
     );
 
     // echo $NOT_UTF8 | rustc -
diff --git a/tests/run-make/stdin-rustdoc/rmake.rs b/tests/run-make/stdin-rustdoc/rmake.rs
index 584a610ed63..a72fe1bc7da 100644
--- a/tests/run-make/stdin-rustdoc/rmake.rs
+++ b/tests/run-make/stdin-rustdoc/rmake.rs
@@ -1,6 +1,7 @@
 //! This test checks rustdoc `-` (stdin) handling
 
-use run_make_support::{rustdoc, tmp_dir};
+use run_make_support::rustdoc;
+use std::path::PathBuf;
 
 static INPUT: &str = r#"
 //! ```
@@ -10,8 +11,7 @@ pub struct F;
 "#;
 
 fn main() {
-    let tmp_dir = tmp_dir();
-    let out_dir = tmp_dir.join("doc");
+    let out_dir = PathBuf::from("doc");
 
     // rustdoc -
     rustdoc().arg("-").out_dir(&out_dir).stdin(INPUT).run();
diff --git a/tests/run-make/suspicious-library/rmake.rs b/tests/run-make/suspicious-library/rmake.rs
index 9e91de70bfc..e789607482b 100644
--- a/tests/run-make/suspicious-library/rmake.rs
+++ b/tests/run-make/suspicious-library/rmake.rs
@@ -3,12 +3,12 @@
 
 //@ ignore-cross-compile
 
-use run_make_support::{dynamic_lib, rustc};
+use run_make_support::{dynamic_lib_name, rustc};
 use std::fs::File;
 
 fn main() {
     rustc().input("foo.rs").arg("-Cprefer-dynamic").run();
-    File::create(dynamic_lib("foo-something-special")).unwrap();
-    File::create(dynamic_lib("foo-something-special2")).unwrap();
+    File::create(dynamic_lib_name("foo-something-special")).unwrap();
+    File::create(dynamic_lib_name("foo-something-special2")).unwrap();
     rustc().input("bar.rs").run();
 }
diff --git a/tests/run-make/wasm-abi/rmake.rs b/tests/run-make/wasm-abi/rmake.rs
index a2dcafbbe0f..0fc326babd9 100644
--- a/tests/run-make/wasm-abi/rmake.rs
+++ b/tests/run-make/wasm-abi/rmake.rs
@@ -1,14 +1,14 @@
 //@ only-wasm32-wasip1
 //@ needs-wasmtime
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 use std::path::Path;
 use std::process::Command;
 
 fn main() {
     rustc().input("foo.rs").target("wasm32-wasip1").run();
 
-    let file = tmp_dir().join("foo.wasm");
+    let file = Path::new("foo.wasm");
 
     run(&file, "return_two_i32", "1\n2\n");
     run(&file, "return_two_i64", "3\n4\n");
diff --git a/tests/run-make/wasm-custom-section/rmake.rs b/tests/run-make/wasm-custom-section/rmake.rs
index 0303ca05ca6..65f6d0bf347 100644
--- a/tests/run-make/wasm-custom-section/rmake.rs
+++ b/tests/run-make/wasm-custom-section/rmake.rs
@@ -1,13 +1,13 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::collections::HashMap;
 
 fn main() {
     rustc().input("foo.rs").target("wasm32-wasip1").run();
     rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run();
 
-    let file = std::fs::read(&tmp_dir().join("bar.wasm")).unwrap();
+    let file = std::fs::read("bar.wasm").unwrap();
 
     let mut custom = HashMap::new();
     for payload in wasmparser::Parser::new(0).parse_all(&file) {
diff --git a/tests/run-make/wasm-custom-sections-opt/rmake.rs b/tests/run-make/wasm-custom-sections-opt/rmake.rs
index 50916b1bf81..8e66caa6d68 100644
--- a/tests/run-make/wasm-custom-sections-opt/rmake.rs
+++ b/tests/run-make/wasm-custom-sections-opt/rmake.rs
@@ -1,13 +1,13 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::collections::HashMap;
 use std::path::Path;
 
 fn main() {
     rustc().input("foo.rs").target("wasm32-wasip1").opt().run();
 
-    verify(&tmp_dir().join("foo.wasm"));
+    verify(&Path::new("foo.wasm"));
 }
 
 fn verify(path: &Path) {
diff --git a/tests/run-make/wasm-export-all-symbols/rmake.rs b/tests/run-make/wasm-export-all-symbols/rmake.rs
index f4c51bc4ab4..a6fec1fb0eb 100644
--- a/tests/run-make/wasm-export-all-symbols/rmake.rs
+++ b/tests/run-make/wasm-export-all-symbols/rmake.rs
@@ -1,6 +1,6 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::collections::HashMap;
 use std::path::Path;
 use wasmparser::ExternalKind::*;
@@ -18,12 +18,9 @@ fn test(args: &[&str]) {
     rustc().input("foo.rs").target("wasm32-wasip1").args(args).run();
     rustc().input("main.rs").target("wasm32-wasip1").args(args).run();
 
+    verify_exports(Path::new("foo.wasm"), &[("foo", Func), ("FOO", Global), ("memory", Memory)]);
     verify_exports(
-        &tmp_dir().join("foo.wasm"),
-        &[("foo", Func), ("FOO", Global), ("memory", Memory)],
-    );
-    verify_exports(
-        &tmp_dir().join("main.wasm"),
+        Path::new("main.wasm"),
         &[
             ("foo", Func),
             ("FOO", Global),
diff --git a/tests/run-make/wasm-import-module/rmake.rs b/tests/run-make/wasm-import-module/rmake.rs
index 6eed229e907..0d3152ae99c 100644
--- a/tests/run-make/wasm-import-module/rmake.rs
+++ b/tests/run-make/wasm-import-module/rmake.rs
@@ -1,6 +1,6 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::collections::HashMap;
 use wasmparser::TypeRef::Func;
 
@@ -8,7 +8,7 @@ fn main() {
     rustc().input("foo.rs").target("wasm32-wasip1").run();
     rustc().input("bar.rs").target("wasm32-wasip1").arg("-Clto").opt().run();
 
-    let file = std::fs::read(&tmp_dir().join("bar.wasm")).unwrap();
+    let file = std::fs::read("bar.wasm").unwrap();
 
     let mut imports = HashMap::new();
     for payload in wasmparser::Parser::new(0).parse_all(&file) {
diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs
index 373b966401c..304e5d04833 100644
--- a/tests/run-make/wasm-panic-small/rmake.rs
+++ b/tests/run-make/wasm-panic-small/rmake.rs
@@ -1,7 +1,7 @@
 //@ only-wasm32-wasip1
 #![deny(warnings)]
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
     test("a");
@@ -15,7 +15,7 @@ fn test(cfg: &str) {
 
     rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().cfg(cfg).run();
 
-    let bytes = std::fs::read(&tmp_dir().join("foo.wasm")).unwrap();
+    let bytes = std::fs::read("foo.wasm").unwrap();
     println!("{}", bytes.len());
     assert!(bytes.len() < 40_000);
 }
diff --git a/tests/run-make/wasm-spurious-import/rmake.rs b/tests/run-make/wasm-spurious-import/rmake.rs
index 458c7bfccb7..eb28fdb2b01 100644
--- a/tests/run-make/wasm-spurious-import/rmake.rs
+++ b/tests/run-make/wasm-spurious-import/rmake.rs
@@ -1,6 +1,6 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::collections::HashMap;
 
 fn main() {
@@ -13,7 +13,7 @@ fn main() {
         .arg("-Copt-level=z")
         .run();
 
-    let file = std::fs::read(&tmp_dir().join("main.wasm")).unwrap();
+    let file = std::fs::read("main.wasm").unwrap();
 
     let mut imports = HashMap::new();
     for payload in wasmparser::Parser::new(0).parse_all(&file) {
diff --git a/tests/run-make/wasm-stringify-ints-small/rmake.rs b/tests/run-make/wasm-stringify-ints-small/rmake.rs
index 9fac0c0c215..8c51e26cc33 100644
--- a/tests/run-make/wasm-stringify-ints-small/rmake.rs
+++ b/tests/run-make/wasm-stringify-ints-small/rmake.rs
@@ -1,12 +1,12 @@
 //@ only-wasm32-wasip1
 #![deny(warnings)]
 
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 
 fn main() {
     rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run();
 
-    let bytes = std::fs::read(&tmp_dir().join("foo.wasm")).unwrap();
+    let bytes = std::fs::read("foo.wasm").unwrap();
     println!("{}", bytes.len());
     assert!(bytes.len() < 50_000);
 }
diff --git a/tests/run-make/wasm-symbols-different-module/rmake.rs b/tests/run-make/wasm-symbols-different-module/rmake.rs
index 521d2c31ee6..83970ef0b24 100644
--- a/tests/run-make/wasm-symbols-different-module/rmake.rs
+++ b/tests/run-make/wasm-symbols-different-module/rmake.rs
@@ -1,7 +1,8 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::collections::{HashMap, HashSet};
+use std::path::Path;
 
 fn main() {
     test_file("foo.rs", &[("a", &["foo"]), ("b", &["foo"])]);
@@ -22,7 +23,7 @@ fn test(file: &str, args: &[&str], expected_imports: &[(&str, &[&str])]) {
 
     rustc().input(file).target("wasm32-wasip1").args(args).run();
 
-    let file = std::fs::read(&tmp_dir().join(file).with_extension("wasm")).unwrap();
+    let file = std::fs::read(Path::new(file).with_extension("wasm")).unwrap();
 
     let mut imports = HashMap::new();
     for payload in wasmparser::Parser::new(0).parse_all(&file) {
diff --git a/tests/run-make/wasm-symbols-not-exported/rmake.rs b/tests/run-make/wasm-symbols-not-exported/rmake.rs
index 1b020b67a38..54604f51a9e 100644
--- a/tests/run-make/wasm-symbols-not-exported/rmake.rs
+++ b/tests/run-make/wasm-symbols-not-exported/rmake.rs
@@ -1,18 +1,18 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::path::Path;
 
 fn main() {
     rustc().input("foo.rs").target("wasm32-wasip1").run();
-    verify_symbols(&tmp_dir().join("foo.wasm"));
+    verify_symbols(Path::new("foo.wasm"));
     rustc().input("foo.rs").target("wasm32-wasip1").opt().run();
-    verify_symbols(&tmp_dir().join("foo.wasm"));
+    verify_symbols(Path::new("foo.wasm"));
 
     rustc().input("bar.rs").target("wasm32-wasip1").run();
-    verify_symbols(&tmp_dir().join("bar.wasm"));
+    verify_symbols(Path::new("bar.wasm"));
     rustc().input("bar.rs").target("wasm32-wasip1").opt().run();
-    verify_symbols(&tmp_dir().join("bar.wasm"));
+    verify_symbols(Path::new("bar.wasm"));
 }
 
 fn verify_symbols(path: &Path) {
diff --git a/tests/run-make/wasm-symbols-not-imported/rmake.rs b/tests/run-make/wasm-symbols-not-imported/rmake.rs
index a653ab61b2c..30408f078bc 100644
--- a/tests/run-make/wasm-symbols-not-imported/rmake.rs
+++ b/tests/run-make/wasm-symbols-not-imported/rmake.rs
@@ -1,17 +1,17 @@
 //@ only-wasm32-wasip1
 
-use run_make_support::{rustc, tmp_dir, wasmparser};
+use run_make_support::{rustc, wasmparser};
 use std::path::Path;
 
 fn main() {
     rustc().input("foo.rs").target("wasm32-wasip1").run();
-    verify_symbols(&tmp_dir().join("foo.wasm"));
+    verify_symbols(Path::new("foo.wasm"));
     rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").run();
-    verify_symbols(&tmp_dir().join("foo.wasm"));
+    verify_symbols(Path::new("foo.wasm"));
     rustc().input("foo.rs").target("wasm32-wasip1").opt().run();
-    verify_symbols(&tmp_dir().join("foo.wasm"));
+    verify_symbols(Path::new("foo.wasm"));
     rustc().input("foo.rs").target("wasm32-wasip1").arg("-Clto").opt().run();
-    verify_symbols(&tmp_dir().join("foo.wasm"));
+    verify_symbols(Path::new("foo.wasm"));
 }
 
 fn verify_symbols(path: &Path) {
diff --git a/tests/run-make/windows-binary-no-external-deps/rmake.rs b/tests/run-make/windows-binary-no-external-deps/rmake.rs
index ccf2d64c853..61fdb202221 100644
--- a/tests/run-make/windows-binary-no-external-deps/rmake.rs
+++ b/tests/run-make/windows-binary-no-external-deps/rmake.rs
@@ -2,7 +2,7 @@
 //! a "hello world" application by setting `PATH` to `C:\Windows\System32`.
 //@ only-windows
 
-use run_make_support::{env_var, rustc, tmp_dir};
+use run_make_support::{cwd, env_var, rustc};
 use std::path::PathBuf;
 use std::process::Command;
 
@@ -12,7 +12,7 @@ fn main() {
     let windows_dir = env_var("SystemRoot");
     let system32: PathBuf = [&windows_dir, "System32"].iter().collect();
     // Note: This does not use the support wrappers so that we can precisely control the PATH
-    let exe = tmp_dir().join("hello.exe");
+    let exe = cwd().join("hello.exe");
     let status = Command::new(exe).env("PATH", &system32).spawn().unwrap().wait().unwrap();
     if !status.success() {
         panic!("Command failed!\noutput status: `{status}`");
diff --git a/tests/run-make/windows-spawn/rmake.rs b/tests/run-make/windows-spawn/rmake.rs
index fb9cf1e2149..a6a7acd7ccb 100644
--- a/tests/run-make/windows-spawn/rmake.rs
+++ b/tests/run-make/windows-spawn/rmake.rs
@@ -1,6 +1,6 @@
 //@ only-windows
 
-use run_make_support::{run, rustc, tmp_dir};
+use run_make_support::{run, rustc};
 
 // On Windows `Command` uses `CreateProcessW` to run a new process.
 // However, in the past std used to not pass in the application name, leaving
@@ -10,8 +10,7 @@ use run_make_support::{run, rustc, tmp_dir};
 // `foo bar.exe` if foo.exe does not exist. Which is clearly not desired.
 
 fn main() {
-    let out_dir = tmp_dir();
-    rustc().input("hello.rs").output(out_dir.join("hopefullydoesntexist bar.exe")).run();
+    rustc().input("hello.rs").output("hopefullydoesntexist bar.exe").run();
     rustc().input("spawn.rs").run();
     run("spawn");
 }
diff --git a/tests/run-make/windows-ws2_32/rmake.rs b/tests/run-make/windows-ws2_32/rmake.rs
index 543f8594478..b3c70c354b4 100644
--- a/tests/run-make/windows-ws2_32/rmake.rs
+++ b/tests/run-make/windows-ws2_32/rmake.rs
@@ -3,7 +3,7 @@
 // Tests that WS2_32.dll is not unnecessarily linked, see issue #85441
 
 use run_make_support::object::{self, read::Object};
-use run_make_support::{rustc, tmp_dir};
+use run_make_support::rustc;
 use std::fs;
 
 fn main() {
@@ -15,8 +15,7 @@ fn main() {
 }
 
 fn links_ws2_32(exe: &str) -> bool {
-    let path = tmp_dir().join(exe);
-    let binary_data = fs::read(path).unwrap();
+    let binary_data = fs::read(exe).unwrap();
     let file = object::File::parse(&*binary_data).unwrap();
     for import in file.imports().unwrap() {
         if import.library().eq_ignore_ascii_case(b"WS2_32.dll") {
diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js
index ffd169812b6..c4d7c2b0b85 100644
--- a/tests/rustdoc-js-std/parser-errors.js
+++ b/tests/rustdoc-js-std/parser-errors.js
@@ -24,7 +24,7 @@ const PARSED = [
         original: "-> *",
         returned: [],
         userQuery: "-> *",
-        error: "Unexpected `*` after ` `",
+        error: "Unexpected `*` after ` ` (not a valid identifier)",
     },
     {
         query: 'a<"P">',
@@ -204,16 +204,25 @@ const PARSED = [
         original: "_:",
         returned: [],
         userQuery: "_:",
-        error: "Unexpected `:` (expected path after type filter `_:`)",
+        error: "Unexpected `_` (not a valid identifier)",
     },
     {
-        query: "_:a",
+        query: "ab:",
         elems: [],
         foundElems: 0,
-        original: "_:a",
+        original: "ab:",
         returned: [],
-        userQuery: "_:a",
-        error: "Unknown type filter `_`",
+        userQuery: "ab:",
+        error: "Unexpected `:` (expected path after type filter `ab:`)",
+    },
+    {
+        query: "a:b",
+        elems: [],
+        foundElems: 0,
+        original: "a:b",
+        returned: [],
+        userQuery: "a:b",
+        error: "Unknown type filter `a`",
     },
     {
         query: "a-bb",
@@ -240,7 +249,7 @@ const PARSED = [
         original: "ab'",
         returned: [],
         userQuery: "ab'",
-        error: "Unexpected `'` after `b`",
+        error: "Unexpected `'` after `b` (not a valid identifier)",
     },
     {
         query: "a->",
diff --git a/tests/rustdoc-js/basic.js b/tests/rustdoc-js/basic.js
index e186d510887..c38b8435c2d 100644
--- a/tests/rustdoc-js/basic.js
+++ b/tests/rustdoc-js/basic.js
@@ -1,6 +1,6 @@
 const EXPECTED = {
     'query': 'Fo',
     'others': [
-        { 'path': 'basic', 'name': 'Foo' },
+        { 'path': 'basic', 'name': 'Foo', 'desc': 'Docs for Foo' },
     ],
 };
diff --git a/tests/rustdoc-js/doc-alias.js b/tests/rustdoc-js/doc-alias.js
index 7e4e8a776d8..e57bd71419d 100644
--- a/tests/rustdoc-js/doc-alias.js
+++ b/tests/rustdoc-js/doc-alias.js
@@ -5,6 +5,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Struct',
+                'desc': 'Doc for <code>Struct</code>',
                 'alias': 'StructItem',
                 'href': '../doc_alias/struct.Struct.html',
                 'is_alias': true
@@ -17,6 +18,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Struct',
                 'name': 'field',
+                'desc': 'Doc for <code>Struct</code>’s <code>field</code>',
                 'alias': 'StructFieldItem',
                 'href': '../doc_alias/struct.Struct.html#structfield.field',
                 'is_alias': true
@@ -29,6 +31,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Struct',
                 'name': 'method',
+                'desc': 'Doc for <code>Struct::method</code>',
                 'alias': 'StructMethodItem',
                 'href': '../doc_alias/struct.Struct.html#method.method',
                 'is_alias': true
@@ -45,6 +48,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Struct',
                 'name': 'ImplConstItem',
+                'desc': 'Doc for <code>Struct::ImplConstItem</code>',
                 'alias': 'StructImplConstItem',
                 'href': '../doc_alias/struct.Struct.html#associatedconstant.ImplConstItem',
                 'is_alias': true
@@ -57,6 +61,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Struct',
                 'name': 'function',
+                'desc': 'Doc for <code>Trait::function</code> implemented for Struct',
                 'alias': 'ImplTraitFunction',
                 'href': '../doc_alias/struct.Struct.html#method.function',
                 'is_alias': true
@@ -69,6 +74,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Enum',
+                'desc': 'Doc for <code>Enum</code>',
                 'alias': 'EnumItem',
                 'href': '../doc_alias/enum.Enum.html',
                 'is_alias': true
@@ -81,6 +87,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Enum',
                 'name': 'Variant',
+                'desc': 'Doc for <code>Enum::Variant</code>',
                 'alias': 'VariantItem',
                 'href': '../doc_alias/enum.Enum.html#variant.Variant',
                 'is_alias': true
@@ -93,6 +100,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Enum',
                 'name': 'method',
+                'desc': 'Doc for <code>Enum::method</code>',
                 'alias': 'EnumMethodItem',
                 'href': '../doc_alias/enum.Enum.html#method.method',
                 'is_alias': true
@@ -105,6 +113,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Typedef',
+                'desc': 'Doc for type alias <code>Typedef</code>',
                 'alias': 'TypedefItem',
                 'href': '../doc_alias/type.Typedef.html',
                 'is_alias': true
@@ -117,6 +126,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Trait',
+                'desc': 'Doc for <code>Trait</code>',
                 'alias': 'TraitItem',
                 'href': '../doc_alias/trait.Trait.html',
                 'is_alias': true
@@ -129,6 +139,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Trait',
                 'name': 'Target',
+                'desc': 'Doc for <code>Trait::Target</code>',
                 'alias': 'TraitTypeItem',
                 'href': '../doc_alias/trait.Trait.html#associatedtype.Target',
                 'is_alias': true
@@ -141,6 +152,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Trait',
                 'name': 'AssociatedConst',
+                'desc': 'Doc for <code>Trait::AssociatedConst</code>',
                 'alias': 'AssociatedConstItem',
                 'href': '../doc_alias/trait.Trait.html#associatedconstant.AssociatedConst',
                 'is_alias': true
@@ -153,6 +165,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Trait',
                 'name': 'function',
+                'desc': 'Doc for <code>Trait::function</code>',
                 'alias': 'TraitFunctionItem',
                 'href': '../doc_alias/trait.Trait.html#tymethod.function',
                 'is_alias': true
@@ -165,6 +178,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'function',
+                'desc': 'Doc for <code>function</code>',
                 'alias': 'FunctionItem',
                 'href': '../doc_alias/fn.function.html',
                 'is_alias': true
@@ -177,6 +191,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Module',
+                'desc': 'Doc for <code>Module</code>',
                 'alias': 'ModuleItem',
                 'href': '../doc_alias/Module/index.html',
                 'is_alias': true
@@ -189,6 +204,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Const',
+                'desc': 'Doc for <code>Const</code>',
                 'alias': 'ConstItem',
                 'href': '../doc_alias/constant.Const.html',
                 'is_alias': true
@@ -205,6 +221,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Static',
+                'desc': 'Doc for <code>Static</code>',
                 'alias': 'StaticItem',
                 'href': '../doc_alias/static.Static.html',
                 'is_alias': true
@@ -217,6 +234,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Union',
+                'desc': 'Doc for <code>Union</code>',
                 'alias': 'UnionItem',
                 'href': '../doc_alias/union.Union.html',
                 'is_alias': true
@@ -225,6 +243,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Union',
                 'name': 'union_item',
+                'desc': 'Doc for <code>Union::union_item</code>',
                 'href': '../doc_alias/union.Union.html#structfield.union_item'
             },
         ],
@@ -235,6 +254,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Union',
                 'name': 'union_item',
+                'desc': 'Doc for <code>Union::union_item</code>',
                 'alias': 'UnionFieldItem',
                 'href': '../doc_alias/union.Union.html#structfield.union_item',
                 'is_alias': true
@@ -247,6 +267,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias::Union',
                 'name': 'method',
+                'desc': 'Doc for <code>Union::method</code>',
                 'alias': 'UnionMethodItem',
                 'href': '../doc_alias/union.Union.html#method.method',
                 'is_alias': true
@@ -259,6 +280,7 @@ const EXPECTED = [
             {
                 'path': 'doc_alias',
                 'name': 'Macro',
+                'desc': 'Doc for <code>Macro</code>',
                 'alias': 'MacroItem',
                 'href': '../doc_alias/macro.Macro.html',
                 'is_alias': true
diff --git a/tests/rustdoc-js/doc-alias.rs b/tests/rustdoc-js/doc-alias.rs
index 453b55c956c..bf075385327 100644
--- a/tests/rustdoc-js/doc-alias.rs
+++ b/tests/rustdoc-js/doc-alias.rs
@@ -1,12 +1,16 @@
+/// Doc for `Struct`
 #[doc(alias = "StructItem")]
 pub struct Struct {
+    /// Doc for `Struct`'s `field`
     #[doc(alias = "StructFieldItem")]
     pub field: u32,
 }
 
 impl Struct {
+    /// Doc for `Struct::ImplConstItem`
     #[doc(alias = "StructImplConstItem")]
     pub const ImplConstItem: i32 = 0;
+    /// Doc for `Struct::method`
     #[doc(alias = "StructMethodItem")]
     pub fn method(&self) {}
 }
@@ -15,61 +19,78 @@ impl Trait for Struct {
     type Target = u32;
     const AssociatedConst: i32 = 12;
 
+    /// Doc for `Trait::function` implemented for Struct
     #[doc(alias = "ImplTraitFunction")]
     fn function() -> Self::Target {
         0
     }
 }
 
+/// Doc for `Enum`
 #[doc(alias = "EnumItem")]
 pub enum Enum {
+    /// Doc for `Enum::Variant`
     #[doc(alias = "VariantItem")]
     Variant,
 }
 
 impl Enum {
+    /// Doc for `Enum::method`
     #[doc(alias = "EnumMethodItem")]
     pub fn method(&self) {}
 }
 
+/// Doc for type alias `Typedef`
 #[doc(alias = "TypedefItem")]
 pub type Typedef = i32;
 
+/// Doc for `Trait`
 #[doc(alias = "TraitItem")]
 pub trait Trait {
+    /// Doc for `Trait::Target`
     #[doc(alias = "TraitTypeItem")]
     type Target;
+    /// Doc for `Trait::AssociatedConst`
     #[doc(alias = "AssociatedConstItem")]
     const AssociatedConst: i32;
 
+    /// Doc for `Trait::function`
     #[doc(alias = "TraitFunctionItem")]
     fn function() -> Self::Target;
 }
 
+/// Doc for `function`
 #[doc(alias = "FunctionItem")]
 pub fn function() {}
 
+/// Doc for `Module`
 #[doc(alias = "ModuleItem")]
 pub mod Module {}
 
+/// Doc for `Const`
 #[doc(alias = "ConstItem")]
 pub const Const: u32 = 0;
 
+/// Doc for `Static`
 #[doc(alias = "StaticItem")]
 pub static Static: u32 = 0;
 
+/// Doc for `Union`
 #[doc(alias = "UnionItem")]
 pub union Union {
+    /// Doc for `Union::union_item`
     #[doc(alias = "UnionFieldItem")]
     pub union_item: u32,
     pub y: f32,
 }
 
 impl Union {
+    /// Doc for `Union::method`
     #[doc(alias = "UnionMethodItem")]
     pub fn method(&self) {}
 }
 
+/// Doc for `Macro`
 #[doc(alias = "MacroItem")]
 #[macro_export]
 macro_rules! Macro {
diff --git a/tests/rustdoc-js/non-english-identifier.js b/tests/rustdoc-js/non-english-identifier.js
new file mode 100644
index 00000000000..1765a69152a
--- /dev/null
+++ b/tests/rustdoc-js/non-english-identifier.js
@@ -0,0 +1,163 @@
+const PARSED = [
+    {
+        query: '中文',
+        elems: [{
+            name: "中文",
+            fullPath: ["中文"],
+            pathWithoutLast: [],
+            pathLast: "中文",
+            generics: [],
+            typeFilter: -1,
+        }],
+        returned: [],
+        foundElems: 1,
+        original: "中文",
+        userQuery: "中文",
+        error: null,
+    },
+    {
+        query: '_0Mixed中英文',
+        elems: [{
+            name: "_0mixed中英文",
+            fullPath: ["_0mixed中英文"],
+            pathWithoutLast: [],
+            pathLast: "_0mixed中英文",
+            generics: [],
+            typeFilter: -1,
+        }],
+        foundElems: 1,
+        original: "_0Mixed中英文",
+        returned: [],
+        userQuery: "_0mixed中英文",
+        error: null,
+    },
+    {
+        query: 'my_crate::中文API',
+        elems: [{
+            name: "my_crate::中文api",
+            fullPath: ["my_crate", "中文api"],
+            pathWithoutLast: ["my_crate"],
+            pathLast: "中文api",
+            generics: [],
+            typeFilter: -1,
+        }],
+        foundElems: 1,
+        original: "my_crate::中文API",
+        returned: [],
+        userQuery: "my_crate::中文api",
+        error: null,
+    },
+    {
+        query: '类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>',
+        elems: [{
+            name: "类型a",
+            fullPath: ["类型a"],
+            pathWithoutLast: [],
+            pathLast: "类型a",
+            generics: [],
+        }, {
+            name: "类型b",
+            fullPath: ["类型b"],
+            pathWithoutLast: [],
+            pathLast: "类型b",
+            generics: [{
+                name: "约束c",
+                fullPath: ["约束c"],
+                pathWithoutLast: [],
+                pathLast: "约束c",
+                generics: [],
+            }],
+        }],
+        foundElems: 3,
+        totalElems: 5,
+        literalSearch: true,
+        original: "类型A,类型B<约束C>->返回类型<关联类型=路径::约束D>",
+        returned: [{
+            name: "返回类型",
+            fullPath: ["返回类型"],
+            pathWithoutLast: [],
+            pathLast: "返回类型",
+            generics: [],
+        }],
+        userQuery: "类型a,类型b<约束c>->返回类型<关联类型=路径::约束d>",
+        error: null,
+    },
+    {
+        query: 'my_crate 中文宏!',
+        elems: [{
+            name: "my_crate 中文宏",
+            fullPath: ["my_crate", "中文宏"],
+            pathWithoutLast: ["my_crate"],
+            pathLast: "中文宏",
+            generics: [],
+            typeFilter: 16,
+        }],
+        foundElems: 1,
+        original: "my_crate 中文宏!",
+        returned: [],
+        userQuery: "my_crate 中文宏!",
+        error: null,
+    },
+    {
+        query: '非法符号——',
+        elems: [],
+        foundElems: 0,
+        original: "非法符号——",
+        returned: [],
+        userQuery: "非法符号——",
+        error: "Unexpected `—` after `号` (not a valid identifier)",
+    }
+]
+const EXPECTED = [
+    {
+        query: '加法',
+        others: [
+            {
+                name: "add",
+                path: "non_english_identifier",
+                is_alias: true,
+                alias: "加法",
+                href: "../non_english_identifier/macro.add.html"
+            },
+            {
+                name: "add",
+                path: "non_english_identifier",
+                is_alias: true,
+                alias: "加法",
+                href: "../non_english_identifier/fn.add.html"
+            },
+            {
+                name: "加法",
+                path: "non_english_identifier",
+                href: "../non_english_identifier/trait.加法.html",
+                desc: "Add"
+            },
+            {
+                name: "中文名称的加法宏",
+                path: "non_english_identifier",
+                href: "../non_english_identifier/macro.中文名称的加法宏.html",
+            },
+            {
+                name: "中文名称的加法API",
+                path: "non_english_identifier",
+                href: "../non_english_identifier/fn.中文名称的加法API.html",
+            }],
+        in_args: [{
+            name: "加上",
+            path: "non_english_identifier::加法",
+            href: "../non_english_identifier/trait.加法.html#tymethod.加上",
+        }],
+        returned: [],
+    },
+    { // Extensive type-based search is still buggy, experimental & work-in-progress.
+        query: '可迭代->可选',
+        others: [{
+            name: "总计",
+            path: "non_english_identifier",
+            href: "../non_english_identifier/fn.总计.html",
+            desc: "“sum”"
+        }],
+        in_args: [],
+        returned: [],
+    },
+];
diff --git a/tests/rustdoc-js/non-english-identifier.rs b/tests/rustdoc-js/non-english-identifier.rs
new file mode 100644
index 00000000000..70b5141472c
--- /dev/null
+++ b/tests/rustdoc-js/non-english-identifier.rs
@@ -0,0 +1,47 @@
+#[doc(alias = "加法")]
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
+
+pub fn 中文名称的加法API(left: usize, right: usize) -> usize {
+    left + right
+}
+
+#[macro_export]
+macro_rules! 中文名称的加法宏 {
+    ($left:expr, $right:expr) => {
+        ($left) + ($right)
+    };
+}
+
+#[doc(alias = "加法")]
+#[macro_export]
+macro_rules! add {
+    ($left:expr, $right:expr) => {
+        ($left) + ($right)
+    };
+}
+
+/// Add
+pub trait 加法<类型> {
+    type 结果;
+    fn 加上(self, 被加数: 类型) -> Self::结果;
+}
+
+/// IntoIterator
+pub trait 可迭代 {
+    type 项;
+    type 转为迭代器: Iterator<Item = Self::项>;
+    fn 迭代(self) -> Self::转为迭代器;
+}
+
+pub type 可选<类型> = Option<类型>;
+
+/// "sum"
+pub fn 总计<集合, 个体>(容器: 集合) -> 可选<集合::项>
+where
+    集合: 可迭代<项 = 个体>,
+    个体: 加法<个体, 结果 = 个体>,
+{
+    容器.迭代().reduce(|累计值, 当前值| 累计值.加上(当前值))
+}
diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs
index 218c7b3e386..f971426723f 100644
--- a/tests/ui-fulldeps/stable-mir/check_instance.rs
+++ b/tests/ui-fulldeps/stable-mir/check_instance.rs
@@ -33,7 +33,7 @@ fn test_stable_mir() -> ControlFlow<()> {
     // Get all items and split generic vs monomorphic items.
     let (generic, mono): (Vec<_>, Vec<_>) =
         items.into_iter().partition(|item| item.requires_monomorphization());
-    assert_eq!(mono.len(), 3, "Expected 2 mono functions and one constant");
+    assert_eq!(mono.len(), 4, "Expected 2 mono functions and one constant");
     assert_eq!(generic.len(), 2, "Expected 2 generic functions");
 
     // For all monomorphic items, get the correspondent instances.
@@ -57,8 +57,9 @@ fn test_body(body: mir::Body) {
     for term in body.blocks.iter().map(|bb| &bb.terminator) {
         match &term.kind {
             Call { func, .. } => {
-                let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else { unreachable!
-                () };
+                let TyKind::RigidTy(ty) = func.ty(body.locals()).unwrap().kind() else {
+                    unreachable!()
+                };
                 let RigidTy::FnDef(def, args) = ty else { unreachable!() };
                 let instance = Instance::resolve(def, &args).unwrap();
                 let mangled_name = instance.mangled_name();
@@ -102,6 +103,9 @@ fn generate_input(path: &str) -> std::io::Result<()> {
     write!(
         file,
         r#"
+
+    struct Foo(());
+
     pub fn ty_param<T>(t: &T) -> T where T: Clone {{
         t.clone()
     }}
@@ -116,6 +120,7 @@ fn generate_input(path: &str) -> std::io::Result<()> {
     }}
 
     pub fn monomorphic() {{
+        Foo(());
         let v = vec![10];
         let dup = ty_param(&v);
         assert_eq!(v, dup);
diff --git a/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs
new file mode 100644
index 00000000000..ce365d1a8b1
--- /dev/null
+++ b/tests/ui/attributes/unsafe/cfg-unsafe-attributes.rs
@@ -0,0 +1,7 @@
+//@ build-pass
+#![feature(unsafe_attributes)]
+
+#[cfg_attr(all(), unsafe(no_mangle))]
+fn a() {}
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
new file mode 100644
index 00000000000..774ce86c096
--- /dev/null
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.rs
@@ -0,0 +1,6 @@
+#![feature(unsafe_attributes)]
+
+#[derive(unsafe(Debug))] //~ ERROR: traits in `#[derive(...)]` don't accept `unsafe(...)`
+struct Foo;
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
new file mode 100644
index 00000000000..fc0daf16790
--- /dev/null
+++ b/tests/ui/attributes/unsafe/derive-unsafe-attributes.stderr
@@ -0,0 +1,8 @@
+error: traits in `#[derive(...)]` don't accept `unsafe(...)`
+  --> $DIR/derive-unsafe-attributes.rs:3:10
+   |
+LL | #[derive(unsafe(Debug))]
+   |          ^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.rs b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
new file mode 100644
index 00000000000..a6c0ea578f2
--- /dev/null
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.rs
@@ -0,0 +1,9 @@
+#![feature(unsafe_attributes)]
+
+#[unsafe(unsafe(no_mangle))]
+//~^ ERROR expected identifier, found keyword `unsafe`
+//~| ERROR cannot find attribute `r#unsafe` in this scope
+//~| ERROR `r#unsafe` is not an unsafe attribute
+fn a() {}
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
new file mode 100644
index 00000000000..1c07a5bf8ba
--- /dev/null
+++ b/tests/ui/attributes/unsafe/double-unsafe-attributes.stderr
@@ -0,0 +1,27 @@
+error: expected identifier, found keyword `unsafe`
+  --> $DIR/double-unsafe-attributes.rs:3:10
+   |
+LL | #[unsafe(unsafe(no_mangle))]
+   |          ^^^^^^ expected identifier, found keyword
+   |
+help: escape `unsafe` to use it as an identifier
+   |
+LL | #[unsafe(r#unsafe(no_mangle))]
+   |          ++
+
+error: cannot find attribute `r#unsafe` in this scope
+  --> $DIR/double-unsafe-attributes.rs:3:10
+   |
+LL | #[unsafe(unsafe(no_mangle))]
+   |          ^^^^^^
+
+error: `r#unsafe` is not an unsafe attribute
+  --> $DIR/double-unsafe-attributes.rs:3:3
+   |
+LL | #[unsafe(unsafe(no_mangle))]
+   |   ^^^^^^
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/attributes/unsafe/unsafe-attributes.rs b/tests/ui/attributes/unsafe/unsafe-attributes.rs
new file mode 100644
index 00000000000..e7620a18048
--- /dev/null
+++ b/tests/ui/attributes/unsafe/unsafe-attributes.rs
@@ -0,0 +1,7 @@
+//@ build-pass
+#![feature(unsafe_attributes)]
+
+#[unsafe(no_mangle)]
+fn a() {}
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs
new file mode 100644
index 00000000000..67db36afd2e
--- /dev/null
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.rs
@@ -0,0 +1,6 @@
+#![feature(unsafe_attributes)]
+
+#[unsafe(repr(C))] //~ ERROR: is not an unsafe attribute
+struct Foo {}
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
new file mode 100644
index 00000000000..0602af34e4f
--- /dev/null
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute.stderr
@@ -0,0 +1,10 @@
+error: `repr` is not an unsafe attribute
+  --> $DIR/unsafe-safe-attribute.rs:3:3
+   |
+LL | #[unsafe(repr(C))]
+   |   ^^^^^^
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
new file mode 100644
index 00000000000..ff2eb61b405
--- /dev/null
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs
@@ -0,0 +1,8 @@
+#![feature(unsafe_attributes)]
+
+#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute
+    message = "testing",
+))]
+trait Foo {}
+
+fn main() {}
diff --git a/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
new file mode 100644
index 00000000000..584dacf4d8c
--- /dev/null
+++ b/tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.stderr
@@ -0,0 +1,10 @@
+error: `diagnostic::on_unimplemented` is not an unsafe attribute
+  --> $DIR/unsafe-safe-attribute_diagnostic.rs:3:3
+   |
+LL | #[unsafe(diagnostic::on_unimplemented(
+   |   ^^^^^^
+   |
+   = note: extraneous unsafe is not allowed in attributes
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/coherence/re-rebalance-coherence.rs b/tests/ui/coherence/re-rebalance-coherence.rs
index 9c176d5b1b1..5383a634617 100644
--- a/tests/ui/coherence/re-rebalance-coherence.rs
+++ b/tests/ui/coherence/re-rebalance-coherence.rs
@@ -4,6 +4,7 @@
 extern crate re_rebalance_coherence_lib as lib;
 use lib::*;
 
+#[allow(dead_code)]
 struct Oracle;
 impl Backend for Oracle {}
 impl<'a, T:'a, Tab> QueryFragment<Oracle> for BatchInsert<'a, T, Tab> {}
diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
index c23187598bc..4bf2fa761ea 100644
--- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
+++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
@@ -2,6 +2,7 @@
 
 //@ run-pass
 
+#[allow(dead_code)]
 #[repr(C)]
 pub struct Loaf<T: Sized, const N: usize = 1> {
     head: [T; N],
diff --git a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
index 5d2198f50ad..50a6102c605 100644
--- a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
+++ b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
@@ -16,7 +16,8 @@ impl BlockCipher for BarCipher {
     const BLOCK_SIZE: usize = 32;
 }
 
-pub struct Block<C>(#[allow(dead_code)] C);
+#[allow(dead_code)]
+pub struct Block<C>(C);
 
 pub fn test<C: BlockCipher, const M: usize>()
 where
diff --git a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
index 419d605d0c8..35c41ae4615 100644
--- a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
+++ b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
@@ -6,6 +6,7 @@
 
 use std::mem::MaybeUninit;
 
+#[allow(dead_code)]
 #[repr(transparent)]
 pub struct MaybeUninitWrapper<const N: usize>(MaybeUninit<[u64; N]>);
 
diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs
index 6ab1fb7b039..885dacc727a 100644
--- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs
+++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs
@@ -1,9 +1,9 @@
 #![forbid(dead_code)]
 
 #[derive(Debug)]
-pub struct Whatever {
+pub struct Whatever { //~ ERROR struct `Whatever` is never constructed
     pub field0: (),
-    field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read
+    field1: (),
     field2: (),
     field3: (),
     field4: (),
diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr
index e9b757b6bae..e10d28ad03a 100644
--- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr
+++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr
@@ -1,19 +1,9 @@
-error: fields `field1`, `field2`, `field3`, and `field4` are never read
-  --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5
+error: struct `Whatever` is never constructed
+  --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12
    |
 LL | pub struct Whatever {
-   |            -------- fields in this struct
-LL |     pub field0: (),
-LL |     field1: (),
-   |     ^^^^^^
-LL |     field2: (),
-   |     ^^^^^^
-LL |     field3: (),
-   |     ^^^^^^
-LL |     field4: (),
-   |     ^^^^^^
+   |            ^^^^^^^^
    |
-   = note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
 note: the lint level is defined here
   --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11
    |
diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs b/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs
new file mode 100644
index 00000000000..9eba415dda0
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-unsafe-attributes.rs
@@ -0,0 +1,8 @@
+#[unsafe(no_mangle)] //~ ERROR [E0658]
+extern "C" fn foo() {
+
+}
+
+fn main() {
+    foo();
+}
diff --git a/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr b/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr
new file mode 100644
index 00000000000..dfcea756b02
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-unsafe-attributes.stderr
@@ -0,0 +1,13 @@
+error[E0658]: `#[unsafe()]` markers for attributes are experimental
+  --> $DIR/feature-gate-unsafe-attributes.rs:1:3
+   |
+LL | #[unsafe(no_mangle)]
+   |   ^^^^^^
+   |
+   = note: see issue #123757 <https://github.com/rust-lang/rust/issues/123757> for more information
+   = help: add `#![feature(unsafe_attributes)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/imports/cycle-import-in-std-1.rs b/tests/ui/imports/cycle-import-in-std-1.rs
new file mode 100644
index 00000000000..2fa492c155d
--- /dev/null
+++ b/tests/ui/imports/cycle-import-in-std-1.rs
@@ -0,0 +1,9 @@
+//@ edition: 2018
+
+// https://github.com/rust-lang/rust/issues/124490
+
+use ops::{self as std};
+//~^ ERROR: unresolved import `ops`
+use std::collections::{self as ops};
+
+fn main() {}
diff --git a/tests/ui/imports/cycle-import-in-std-1.stderr b/tests/ui/imports/cycle-import-in-std-1.stderr
new file mode 100644
index 00000000000..d4e6f32cc10
--- /dev/null
+++ b/tests/ui/imports/cycle-import-in-std-1.stderr
@@ -0,0 +1,13 @@
+error[E0432]: unresolved import `ops`
+  --> $DIR/cycle-import-in-std-1.rs:5:11
+   |
+LL | use ops::{self as std};
+   |           ^^^^^^^^^^^ no external crate `ops`
+   |
+   = help: consider importing one of these items instead:
+           core::ops
+           std::ops
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/imports/cycle-import-in-std-2.rs b/tests/ui/imports/cycle-import-in-std-2.rs
new file mode 100644
index 00000000000..d73c57944bc
--- /dev/null
+++ b/tests/ui/imports/cycle-import-in-std-2.rs
@@ -0,0 +1,9 @@
+//@ edition: 2018
+
+// https://github.com/rust-lang/rust/issues/125013
+
+use ops::{self as std};
+//~^ ERROR: unresolved import `ops`
+use std::ops::Deref::{self as ops};
+
+fn main() {}
diff --git a/tests/ui/imports/cycle-import-in-std-2.stderr b/tests/ui/imports/cycle-import-in-std-2.stderr
new file mode 100644
index 00000000000..dc0270dffe4
--- /dev/null
+++ b/tests/ui/imports/cycle-import-in-std-2.stderr
@@ -0,0 +1,13 @@
+error[E0432]: unresolved import `ops`
+  --> $DIR/cycle-import-in-std-2.rs:5:11
+   |
+LL | use ops::{self as std};
+   |           ^^^^^^^^^^^ no external crate `ops`
+   |
+   = help: consider importing one of these items instead:
+           core::ops
+           std::ops
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/tests/ui/inline-const/const_block_pat_liveness.rs b/tests/ui/inline-const/const_block_pat_liveness.rs
new file mode 100644
index 00000000000..26393a4f65b
--- /dev/null
+++ b/tests/ui/inline-const/const_block_pat_liveness.rs
@@ -0,0 +1,18 @@
+//! This test used to ICE because const blocks didn't have a body
+//! anymore, making a lot of logic very fragile around handling the
+//! HIR of a const block.
+//! https://github.com/rust-lang/rust/issues/125846
+
+//@ check-pass
+
+#![feature(inline_const_pat)]
+
+fn main() {
+    match 0 {
+        const {
+            let a = 10_usize;
+            *&a
+        }
+        | _ => {}
+    }
+}
diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs
index ce9ef78ffcd..89ea9fbdcd8 100644
--- a/tests/ui/issues/issue-5708.rs
+++ b/tests/ui/issues/issue-5708.rs
@@ -44,6 +44,7 @@ pub trait MyTrait<T> {
     fn dummy(&self, t: T) -> T { panic!() }
 }
 
+#[allow(dead_code)]
 pub struct MyContainer<'a, T:'a> {
     foos: Vec<&'a (dyn MyTrait<T>+'a)> ,
 }
diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs
index ddcafedf7bc..3386dfa4747 100644
--- a/tests/ui/lint/dead-code/lint-dead-code-1.rs
+++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs
@@ -46,11 +46,10 @@ struct SemiUsedStruct;
 impl SemiUsedStruct {
     fn la_la_la() {}
 }
-struct StructUsedAsField;
+struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed
 pub struct StructUsedInEnum;
 struct StructUsedInGeneric;
-pub struct PubStruct2 {
-    #[allow(dead_code)]
+pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed
     struct_used_as_field: *const StructUsedAsField
 }
 
diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr
index eb728b5b930..b0163df8855 100644
--- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr
+++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr
@@ -22,14 +22,26 @@ error: struct `PrivStruct` is never constructed
 LL | struct PrivStruct;
    |        ^^^^^^^^^^
 
+error: struct `StructUsedAsField` is never constructed
+  --> $DIR/lint-dead-code-1.rs:49:8
+   |
+LL | struct StructUsedAsField;
+   |        ^^^^^^^^^^^^^^^^^
+
+error: struct `PubStruct2` is never constructed
+  --> $DIR/lint-dead-code-1.rs:52:12
+   |
+LL | pub struct PubStruct2 {
+   |            ^^^^^^^^^^
+
 error: enum `priv_enum` is never used
-  --> $DIR/lint-dead-code-1.rs:64:6
+  --> $DIR/lint-dead-code-1.rs:63:6
    |
 LL | enum priv_enum { foo2, bar2 }
    |      ^^^^^^^^^
 
 error: variant `bar3` is never constructed
-  --> $DIR/lint-dead-code-1.rs:67:5
+  --> $DIR/lint-dead-code-1.rs:66:5
    |
 LL | enum used_enum {
    |      --------- variant in this enum
@@ -38,25 +50,25 @@ LL |     bar3
    |     ^^^^
 
 error: function `priv_fn` is never used
-  --> $DIR/lint-dead-code-1.rs:88:4
+  --> $DIR/lint-dead-code-1.rs:87:4
    |
 LL | fn priv_fn() {
    |    ^^^^^^^
 
 error: function `foo` is never used
-  --> $DIR/lint-dead-code-1.rs:93:4
+  --> $DIR/lint-dead-code-1.rs:92:4
    |
 LL | fn foo() {
    |    ^^^
 
 error: function `bar` is never used
-  --> $DIR/lint-dead-code-1.rs:98:4
+  --> $DIR/lint-dead-code-1.rs:97:4
    |
 LL | fn bar() {
    |    ^^^
 
 error: function `baz` is never used
-  --> $DIR/lint-dead-code-1.rs:102:4
+  --> $DIR/lint-dead-code-1.rs:101:4
    |
 LL | fn baz() -> impl Copy {
    |    ^^^
@@ -67,5 +79,5 @@ error: struct `Bar` is never constructed
 LL |     pub struct Bar;
    |                ^^^
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/lint/dead-code/unused-assoc-const.rs b/tests/ui/lint/dead-code/unused-assoc-const.rs
new file mode 100644
index 00000000000..36e8315ad36
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-assoc-const.rs
@@ -0,0 +1,20 @@
+#![deny(dead_code)]
+
+trait Trait {
+    const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used
+    const USED_CONST: i32;
+
+    fn foo(&self) {}
+}
+
+pub struct T(());
+
+impl Trait for T {
+    const UNUSED_CONST: i32 = 0;
+    const USED_CONST: i32 = 1;
+}
+
+fn main() {
+    T(()).foo();
+    T::USED_CONST;
+}
diff --git a/tests/ui/lint/dead-code/unused-assoc-const.stderr b/tests/ui/lint/dead-code/unused-assoc-const.stderr
new file mode 100644
index 00000000000..78296d70663
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-assoc-const.stderr
@@ -0,0 +1,16 @@
+error: associated constant `UNUSED_CONST` is never used
+  --> $DIR/unused-assoc-const.rs:4:11
+   |
+LL | trait Trait {
+   |       ----- associated constant in this trait
+LL |     const UNUSED_CONST: i32;
+   |           ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-assoc-const.rs:1:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lint/dead-code/unused-pub-struct.rs b/tests/ui/lint/dead-code/unused-pub-struct.rs
new file mode 100644
index 00000000000..aaf4dd612de
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-pub-struct.rs
@@ -0,0 +1,48 @@
+#![deny(dead_code)]
+
+pub struct NotLint1(());
+pub struct NotLint2(std::marker::PhantomData<i32>);
+
+pub struct NeverConstructed(i32); //~ ERROR struct `NeverConstructed` is never constructed
+
+impl NeverConstructed {
+    pub fn not_construct_self(&self) {}
+}
+
+impl Clone for NeverConstructed {
+    fn clone(&self) -> NeverConstructed {
+        NeverConstructed(0)
+    }
+}
+
+pub trait Trait {
+    fn not_construct_self(&self);
+}
+
+impl Trait for NeverConstructed {
+    fn not_construct_self(&self) {
+        self.0;
+    }
+}
+
+pub struct Constructed(i32);
+
+impl Constructed {
+    pub fn construct_self() -> Self {
+        Constructed(0)
+    }
+}
+
+impl Clone for Constructed {
+    fn clone(&self) -> Constructed {
+        Constructed(0)
+    }
+}
+
+impl Trait for Constructed {
+    fn not_construct_self(&self) {
+        self.0;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-pub-struct.stderr b/tests/ui/lint/dead-code/unused-pub-struct.stderr
new file mode 100644
index 00000000000..3667ddb97bd
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-pub-struct.stderr
@@ -0,0 +1,14 @@
+error: struct `NeverConstructed` is never constructed
+  --> $DIR/unused-pub-struct.rs:6:12
+   |
+LL | pub struct NeverConstructed(i32);
+   |            ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-pub-struct.rs:1:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lint/non-local-defs/consts.stderr b/tests/ui/lint/non-local-defs/consts.stderr
index 48bdaa60823..2756ea40138 100644
--- a/tests/ui/lint/non-local-defs/consts.stderr
+++ b/tests/ui/lint/non-local-defs/consts.stderr
@@ -67,13 +67,18 @@ LL |     impl Test {
 warning: non-local `impl` definition, `impl` blocks should be written at the same level as their item
   --> $DIR/consts.rs:50:9
    |
-LL | fn main() {
-   | --------- move the `impl` block outside of this function `main`
-...
-LL |         impl Test {
-   |         ^^^^^----
-   |              |
-   |              `Test` is not local
+LL |       const {
+   |  ___________-
+LL | |         impl Test {
+   | |         ^^^^^----
+   | |              |
+   | |              `Test` is not local
+LL | |
+LL | |             fn hoo() {}
+...  |
+LL | |         1
+LL | |     };
+   | |_____- move the `impl` block outside of this inline constant `<unnameable>` and up 2 bodies
    |
    = note: methods and associated constants are still usable outside the current expression, only `impl Local` and `impl dyn Local` can ever be private, and only if the type is nested in the same item as the `impl`
    = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>
diff --git a/tests/ui/lint/unreachable_pub.rs b/tests/ui/lint/unreachable_pub.rs
index 22c091e112b..f21f6640342 100644
--- a/tests/ui/lint/unreachable_pub.rs
+++ b/tests/ui/lint/unreachable_pub.rs
@@ -9,12 +9,16 @@ mod private_mod {
     pub use std::env::{Args}; // braced-use has different item spans than unbraced
     //~^ WARNING unreachable_pub
 
+    // we lint on struct definition
     pub struct Hydrogen { //~ WARNING unreachable_pub
-        // `pub` struct fields, too
-        pub neutrons: usize, //~ WARNING unreachable_pub
-        // (... but not more-restricted fields)
+        // but not on fields, even if they are `pub` as putting `pub(crate)`
+        // it would clutter the source code for little value
+        pub neutrons: usize,
         pub(crate) electrons: usize
     }
+    pub(crate) struct Calcium {
+        pub neutrons: usize,
+    }
     impl Hydrogen {
         // impls, too
         pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub
diff --git a/tests/ui/lint/unreachable_pub.stderr b/tests/ui/lint/unreachable_pub.stderr
index 762834b97b9..705a537a3f1 100644
--- a/tests/ui/lint/unreachable_pub.stderr
+++ b/tests/ui/lint/unreachable_pub.stderr
@@ -24,7 +24,7 @@ LL |     pub use std::env::{Args}; // braced-use has different item spans than u
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:12:5
+  --> $DIR/unreachable_pub.rs:13:5
    |
 LL |     pub struct Hydrogen {
    |     ---^^^^^^^^^^^^^^^^
@@ -33,16 +33,8 @@ LL |     pub struct Hydrogen {
    |
    = help: or consider exporting it for use by other crates
 
-warning: unreachable `pub` field
-  --> $DIR/unreachable_pub.rs:14:9
-   |
-LL |         pub neutrons: usize,
-   |         ---^^^^^^^^^^^^^^^^
-   |         |
-   |         help: consider restricting its visibility: `pub(crate)`
-
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:20:9
+  --> $DIR/unreachable_pub.rs:24:9
    |
 LL |         pub fn count_neutrons(&self) -> usize { self.neutrons }
    |         ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -50,7 +42,7 @@ LL |         pub fn count_neutrons(&self) -> usize { self.neutrons }
    |         help: consider restricting its visibility: `pub(crate)`
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:29:5
+  --> $DIR/unreachable_pub.rs:33:5
    |
 LL |     pub enum Helium {}
    |     ---^^^^^^^^^^^^
@@ -60,7 +52,7 @@ LL |     pub enum Helium {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:30:5
+  --> $DIR/unreachable_pub.rs:34:5
    |
 LL |     pub union Lithium { c1: usize, c2: u8 }
    |     ---^^^^^^^^^^^^^^
@@ -70,7 +62,7 @@ LL |     pub union Lithium { c1: usize, c2: u8 }
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:31:5
+  --> $DIR/unreachable_pub.rs:35:5
    |
 LL |     pub fn beryllium() {}
    |     ---^^^^^^^^^^^^^^^
@@ -80,7 +72,7 @@ LL |     pub fn beryllium() {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:32:5
+  --> $DIR/unreachable_pub.rs:36:5
    |
 LL |     pub trait Boron {}
    |     ---^^^^^^^^^^^^
@@ -90,7 +82,7 @@ LL |     pub trait Boron {}
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:33:5
+  --> $DIR/unreachable_pub.rs:37:5
    |
 LL |     pub const CARBON: usize = 1;
    |     ---^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +92,7 @@ LL |     pub const CARBON: usize = 1;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:34:5
+  --> $DIR/unreachable_pub.rs:38:5
    |
 LL |     pub static NITROGEN: usize = 2;
    |     ---^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,7 +102,7 @@ LL |     pub static NITROGEN: usize = 2;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:35:5
+  --> $DIR/unreachable_pub.rs:39:5
    |
 LL |     pub type Oxygen = bool;
    |     ---^^^^^^^^^^^^
@@ -120,7 +112,7 @@ LL |     pub type Oxygen = bool;
    = help: or consider exporting it for use by other crates
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:38:47
+  --> $DIR/unreachable_pub.rs:42:47
    |
 LL |         ($visibility: vis, $name: ident) => { $visibility struct $name {} }
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -135,7 +127,7 @@ LL |     define_empty_struct_with_visibility!(pub, Fluorine);
    = note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: unreachable `pub` item
-  --> $DIR/unreachable_pub.rs:44:9
+  --> $DIR/unreachable_pub.rs:48:9
    |
 LL |         pub fn catalyze() -> bool;
    |         ---^^^^^^^^^^^^^^^^^^^^^^
@@ -144,5 +136,5 @@ LL |         pub fn catalyze() -> bool;
    |
    = help: or consider exporting it for use by other crates
 
-warning: 14 warnings emitted
+warning: 13 warnings emitted
 
diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs
index 0f9eac93930..ba32fb566e8 100644
--- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs
+++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.rs
@@ -20,10 +20,10 @@ fn doc_comment_between_if_else(num: u8) -> bool {
 }
 
 fn doc_comment_on_expr(num: u8) -> bool {
-    (/// useless doc comment
+    /// useless doc comment
     //~^ ERROR: attributes on expressions are experimental
     //~| ERROR: unused doc comment
-    num) == 3
+    num == 3
 }
 
 fn doc_comment_on_expr_field() -> bool {
diff --git a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr
index 1ff5c8d8825..c0741174554 100644
--- a/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr
+++ b/tests/ui/lint/unused/unused-doc-comments-edge-cases.stderr
@@ -5,10 +5,10 @@ LL |     else {
    |     ^^^^ expected expression
 
 error[E0658]: attributes on expressions are experimental
-  --> $DIR/unused-doc-comments-edge-cases.rs:23:6
+  --> $DIR/unused-doc-comments-edge-cases.rs:23:5
    |
-LL |     (/// useless doc comment
-   |      ^^^^^^^^^^^^^^^^^^^^^^^
+LL |     /// useless doc comment
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
    = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
@@ -32,12 +32,12 @@ LL | #![deny(unused_doc_comments)]
    |         ^^^^^^^^^^^^^^^^^^^
 
 error: unused doc comment
-  --> $DIR/unused-doc-comments-edge-cases.rs:23:6
+  --> $DIR/unused-doc-comments-edge-cases.rs:23:5
    |
-LL |     (/// useless doc comment
-   |      ^^^^^^^^^^^^^^^^^^^^^^^
+LL |     /// useless doc comment
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
 ...
-LL |     num) == 3
+LL |     num == 3
    |     --- rustdoc does not generate documentation for expressions
    |
    = help: use `//` for a plain comment
diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed b/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed
deleted file mode 100644
index aae71ede771..00000000000
--- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.fixed
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ run-rustfix
-#![feature(stmt_expr_attributes)]
-#![allow(unused_assignments, unused_attributes)]
-
-fn main() {
-    let mut x = (#[deprecated] 1) + 2; //~ ERROR ambiguous
-
-    (#[deprecated] x) = 4; //~ ERROR ambiguous
-
-    x = (#[deprecated] 5) as i32; //~ ERROR ambiguous
-
-    let _r = (#[deprecated] 1)..6; //~ ERROR ambiguous
-
-    println!("{}", x);
-}
diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs b/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs
deleted file mode 100644
index 613e01d743b..00000000000
--- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ run-rustfix
-#![feature(stmt_expr_attributes)]
-#![allow(unused_assignments, unused_attributes)]
-
-fn main() {
-    let mut x = #[deprecated] 1 + 2; //~ ERROR ambiguous
-
-    #[deprecated] x = 4; //~ ERROR ambiguous
-
-    x = #[deprecated] 5 as i32; //~ ERROR ambiguous
-
-    let _r = #[deprecated] 1..6; //~ ERROR ambiguous
-
-    println!("{}", x);
-}
diff --git a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr b/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr
deleted file mode 100644
index 0430570fd49..00000000000
--- a/tests/ui/parser/attribute/attr-binary-expr-ambigous.stderr
+++ /dev/null
@@ -1,46 +0,0 @@
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:6:17
-   |
-LL |     let mut x = #[deprecated] 1 + 2;
-   |                 ^^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     let mut x = (#[deprecated] 1) + 2;
-   |                 +               +
-
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:8:5
-   |
-LL |     #[deprecated] x = 4;
-   |     ^^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     (#[deprecated] x) = 4;
-   |     +               +
-
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:10:9
-   |
-LL |     x = #[deprecated] 5 as i32;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     x = (#[deprecated] 5) as i32;
-   |         +               +
-
-error: ambiguous outer attributes
-  --> $DIR/attr-binary-expr-ambigous.rs:12:14
-   |
-LL |     let _r = #[deprecated] 1..6;
-   |              ^^^^^^^^^^^^^^^^^^
-   |
-help: wrap the expression in parentheses
-   |
-LL |     let _r = (#[deprecated] 1)..6;
-   |              +               +
-
-error: aborting due to 4 previous errors
-
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
index a851300a982..e9c89807fa5 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
@@ -1,7 +1,8 @@
 // Regression test for issues #100790 and #106439.
 //@ run-rustfix
 
-pub struct Example(#[allow(dead_code)] usize)
+#[allow(dead_code)]
+pub struct Example(usize)
 where
     (): Sized;
 //~^^^ ERROR where clauses are not allowed before tuple struct bodies
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
index 10f435859f1..3bd0f51ec2c 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
@@ -1,10 +1,11 @@
 // Regression test for issues #100790 and #106439.
 //@ run-rustfix
 
+#[allow(dead_code)]
 pub struct Example
 where
     (): Sized,
-(#[allow(dead_code)] usize);
+(usize);
 //~^^^ ERROR where clauses are not allowed before tuple struct bodies
 
 struct _Demo
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
index ddbf237e866..77eafa6bea3 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
@@ -1,23 +1,23 @@
 error: where clauses are not allowed before tuple struct bodies
-  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1
+  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1
    |
 LL |   pub struct Example
    |              ------- while parsing this tuple struct
 LL | / where
 LL | |     (): Sized,
    | |______________^ unexpected where clause
-LL |   (#[allow(dead_code)] usize);
-   |   --------------------------- the struct body
+LL |   (usize);
+   |   ------- the struct body
    |
 help: move the body before the where clause
    |
-LL ~ pub struct Example(#[allow(dead_code)] usize)
+LL ~ pub struct Example(usize)
 LL | where
 LL ~     (): Sized;
    |
 
 error: where clauses are not allowed before tuple struct bodies
-  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1
+  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1
    |
 LL |   struct _Demo
    |          ----- while parsing this tuple struct
diff --git a/tests/ui/proc-macro/issue-81555.rs b/tests/ui/proc-macro/issue-81555.rs
index b337ab7ce37..7a61a31952f 100644
--- a/tests/ui/proc-macro/issue-81555.rs
+++ b/tests/ui/proc-macro/issue-81555.rs
@@ -10,5 +10,6 @@ use test_macros::identity_attr;
 fn main() {
     let _x;
     let y = ();
-    (#[identity_attr] _x) = y;
+    #[identity_attr]
+    _x = y;
 }
diff --git a/tests/ui/pub/pub-ident-struct-4.fixed b/tests/ui/pub/pub-ident-struct-4.fixed
index 5fedbb72437..a62ece43ece 100644
--- a/tests/ui/pub/pub-ident-struct-4.fixed
+++ b/tests/ui/pub/pub-ident-struct-4.fixed
@@ -1,6 +1,7 @@
 //@ run-rustfix
 
-pub struct T(#[allow(dead_code)] String);
+#[allow(dead_code)]
+pub struct T(String);
 //~^ ERROR missing `struct` for struct definition
 
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct-4.rs b/tests/ui/pub/pub-ident-struct-4.rs
index 5c721c25a78..0d56a31beaf 100644
--- a/tests/ui/pub/pub-ident-struct-4.rs
+++ b/tests/ui/pub/pub-ident-struct-4.rs
@@ -1,6 +1,7 @@
 //@ run-rustfix
 
-pub T(#[allow(dead_code)] String);
+#[allow(dead_code)]
+pub T(String);
 //~^ ERROR missing `struct` for struct definition
 
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr
index 5fbb02c8673..ec136783211 100644
--- a/tests/ui/pub/pub-ident-struct-4.stderr
+++ b/tests/ui/pub/pub-ident-struct-4.stderr
@@ -1,12 +1,12 @@
 error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct-4.rs:3:4
+  --> $DIR/pub-ident-struct-4.rs:4:4
    |
-LL | pub T(#[allow(dead_code)] String);
+LL | pub T(String);
    |    ^
    |
 help: add `struct` here to parse `T` as a public struct
    |
-LL | pub struct T(#[allow(dead_code)] String);
+LL | pub struct T(String);
    |     ++++++
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/regions/regions-issue-21422.rs b/tests/ui/regions/regions-issue-21422.rs
index 54beed9b3ac..67852a6f5de 100644
--- a/tests/ui/regions/regions-issue-21422.rs
+++ b/tests/ui/regions/regions-issue-21422.rs
@@ -5,6 +5,7 @@
 
 //@ pretty-expanded FIXME #23616
 
+#[allow(dead_code)]
 pub struct P<'a> {
     _ptr: *const &'a u8,
 }
diff --git a/tests/ui/structs-enums/newtype-struct-with-dtor.rs b/tests/ui/structs-enums/newtype-struct-with-dtor.rs
index 19672e41c9a..16439a7fedd 100644
--- a/tests/ui/structs-enums/newtype-struct-with-dtor.rs
+++ b/tests/ui/structs-enums/newtype-struct-with-dtor.rs
@@ -3,8 +3,10 @@
 #![allow(unused_variables)]
 //@ pretty-expanded FIXME #23616
 
+#[allow(dead_code)]
 pub struct Fd(u32);
 
+#[allow(dead_code)]
 fn foo(a: u32) {}
 
 impl Drop for Fd {
diff --git a/tests/ui/structs-enums/uninstantiable-struct.rs b/tests/ui/structs-enums/uninstantiable-struct.rs
index 97bc7d8414e..1074dbcd6e6 100644
--- a/tests/ui/structs-enums/uninstantiable-struct.rs
+++ b/tests/ui/structs-enums/uninstantiable-struct.rs
@@ -1,4 +1,5 @@
 //@ run-pass
-pub struct Z(#[allow(dead_code)] &'static Z);
+#[allow(dead_code)]
+pub struct Z(&'static Z);
 
 pub fn main() {}
diff --git a/tests/ui/suggestions/derive-clone-for-eq.fixed b/tests/ui/suggestions/derive-clone-for-eq.fixed
index 4dc362f9478..cf800c6e47d 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.fixed
+++ b/tests/ui/suggestions/derive-clone-for-eq.fixed
@@ -1,6 +1,7 @@
 //@ run-rustfix
 // https://github.com/rust-lang/rust/issues/79076
 
+#[allow(dead_code)]
 #[derive(Clone, Eq)] //~ ERROR [E0277]
 pub struct Struct<T: std::clone::Clone>(T);
 
diff --git a/tests/ui/suggestions/derive-clone-for-eq.rs b/tests/ui/suggestions/derive-clone-for-eq.rs
index b3635000f16..84736426bac 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.rs
+++ b/tests/ui/suggestions/derive-clone-for-eq.rs
@@ -1,6 +1,7 @@
 //@ run-rustfix
 // https://github.com/rust-lang/rust/issues/79076
 
+#[allow(dead_code)]
 #[derive(Clone, Eq)] //~ ERROR [E0277]
 pub struct Struct<T>(T);
 
diff --git a/tests/ui/suggestions/derive-clone-for-eq.stderr b/tests/ui/suggestions/derive-clone-for-eq.stderr
index 6fae6e1316d..54670fbffcf 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.stderr
+++ b/tests/ui/suggestions/derive-clone-for-eq.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `T: Clone` is not satisfied
-  --> $DIR/derive-clone-for-eq.rs:4:17
+  --> $DIR/derive-clone-for-eq.rs:5:17
    |
 LL | #[derive(Clone, Eq)]
    |                 ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct<T>: PartialEq`
    |
 note: required for `Struct<T>` to implement `PartialEq`
-  --> $DIR/derive-clone-for-eq.rs:7:19
+  --> $DIR/derive-clone-for-eq.rs:8:19
    |
 LL | impl<T: Clone, U> PartialEq<U> for Struct<T>
    |         -----     ^^^^^^^^^^^^     ^^^^^^^^^
diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed
index 4a5a9483c20..ef07d55871e 100644
--- a/tests/ui/suggestions/option-content-move.fixed
+++ b/tests/ui/suggestions/option-content-move.fixed
@@ -1,4 +1,5 @@
 //@ run-rustfix
+#[allow(dead_code)]
 pub struct LipogramCorpora {
     selections: Vec<(char, Option<String>)>,
 }
@@ -17,6 +18,7 @@ impl LipogramCorpora {
     }
 }
 
+#[allow(dead_code)]
 pub struct LipogramCorpora2 {
     selections: Vec<(char, Result<String, String>)>,
 }
diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs
index 90d05c74399..5be6358fd6a 100644
--- a/tests/ui/suggestions/option-content-move.rs
+++ b/tests/ui/suggestions/option-content-move.rs
@@ -1,4 +1,5 @@
 //@ run-rustfix
+#[allow(dead_code)]
 pub struct LipogramCorpora {
     selections: Vec<(char, Option<String>)>,
 }
@@ -17,6 +18,7 @@ impl LipogramCorpora {
     }
 }
 
+#[allow(dead_code)]
 pub struct LipogramCorpora2 {
     selections: Vec<(char, Result<String, String>)>,
 }
diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr
index a382a04344a..b4ec5b180d2 100644
--- a/tests/ui/suggestions/option-content-move.stderr
+++ b/tests/ui/suggestions/option-content-move.stderr
@@ -1,5 +1,5 @@
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:10:20
+  --> $DIR/option-content-move.rs:11:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@@ -19,7 +19,7 @@ LL |                 if selection.1.clone().unwrap().contains(selection.0) {
    |                               ++++++++
 
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:28:20
+  --> $DIR/option-content-move.rs:30:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
diff --git a/tests/ui/traits/object/generics.rs b/tests/ui/traits/object/generics.rs
index 462b0bc5bb7..0ae562c0d30 100644
--- a/tests/ui/traits/object/generics.rs
+++ b/tests/ui/traits/object/generics.rs
@@ -7,6 +7,7 @@ pub trait Trait2<A> {
     fn doit(&self) -> A;
 }
 
+#[allow(dead_code)]
 pub struct Impl<A1, A2, A3> {
     m1: marker::PhantomData<(A1,A2,A3)>,
     /*
diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout
index 9e45f57af35..137a8aa5510 100644
--- a/tests/ui/unpretty/expanded-exhaustive.stdout
+++ b/tests/ui/unpretty/expanded-exhaustive.stdout
@@ -83,8 +83,7 @@ mod expressions {
     fn expr_const_block() {
         const {};
         const { 1 };
-        const
-            {
+        const {
                 struct S;
             };
     }