diff --git a/.editorconfig b/.editorconfig
index eadd72e49937..ef8ed24c52a5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,6 +9,8 @@ end_of_line = lf
 charset = utf-8
 trim_trailing_whitespace = true
 insert_final_newline = true
+
+[!src/llvm-project]
 indent_style = space
 indent_size = 4
 
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 8612a40923a8..af071c706856 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -29,3 +29,7 @@ ec2cc761bc7067712ecc7734502f703fe3b024c8
 99cb0c6bc399fb94a0ddde7e9b38e9c00d523bad
 # reformat with rustfmt edition 2024
 c682aa162b0d41e21cc6748f4fecfe01efb69d1f
+# reformat with updated edition 2024
+1fcae03369abb4c2cc180cd5a49e1f4440a81300
+# Breaking up of compiletest runtest.rs
+60600a6fa403216bfd66e04f948b1822f6450af7
diff --git a/.gitattributes b/.gitattributes
index d29c15fe712f..8700d5d6dcff 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -5,6 +5,7 @@
 *.h rust
 *.rs rust diff=rust
 *.fixed linguist-language=Rust
+*.pp linguist-language=Rust
 *.mir linguist-language=Rust
 src/etc/installer/gfx/* binary
 src/vendor/** -text
diff --git a/.github/ISSUE_TEMPLATE/bootstrap.md b/.github/ISSUE_TEMPLATE/bootstrap.md
index 8d72eae85931..344ea30e1fc0 100644
--- a/.github/ISSUE_TEMPLATE/bootstrap.md
+++ b/.github/ISSUE_TEMPLATE/bootstrap.md
@@ -32,7 +32,7 @@ Describe what you expected to happen.
 Describe what actually happened.
 -->
 
-### Bootstrap configuration (config.toml)
+### Bootstrap configuration (bootstrap.toml)
 ```toml
 <config>
 ```
diff --git a/.github/ISSUE_TEMPLATE/library_tracking_issue.md b/.github/ISSUE_TEMPLATE/library_tracking_issue.md
index 934312662beb..d56da9d5d025 100644
--- a/.github/ISSUE_TEMPLATE/library_tracking_issue.md
+++ b/.github/ISSUE_TEMPLATE/library_tracking_issue.md
@@ -2,7 +2,7 @@
 name: Library Tracking Issue
 about: A tracking issue for an unstable library feature.
 title: Tracking Issue for XXX
-labels: C-tracking-issue, T-libs-api
+labels: C-tracking-issue, T-libs-api, S-tracking-unimplemented
 ---
 <!--
 Thank you for creating a tracking issue!
@@ -49,6 +49,8 @@ For larger features, more steps might be involved.
 If the feature is changed later, please add those PRs here as well.
 -->
 
+(Remember to update the `S-tracking-*` label when checking boxes.)
+
 - [ ] Implementation: #...
 - [ ] Final comment period (FCP)[^1]
 - [ ] Stabilization PR
diff --git a/.github/ISSUE_TEMPLATE/tracking_issue.md b/.github/ISSUE_TEMPLATE/tracking_issue.md
index 3a9d8408b3c9..aedc15a54c27 100644
--- a/.github/ISSUE_TEMPLATE/tracking_issue.md
+++ b/.github/ISSUE_TEMPLATE/tracking_issue.md
@@ -41,7 +41,10 @@ for larger features an implementation could be broken up into multiple PRs.
 - [ ] Implement the RFC (cc @rust-lang/XXX -- can anyone write up mentoring
       instructions?)
 - [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide])
-- [ ] Formatting for new syntax has been added to the [Style Guide] ([nightly-style-procedure])
+- [ ] Style updates for any new syntax ([nightly-style-procedure])
+  - [ ] Style team decision on new formatting
+  - [ ] Formatting for new syntax has been added to the [Style Guide]
+  - [ ] (non-blocking) Formatting has been implemented in `rustfmt`
 - [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide])
 
 [stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c650df6a0ec2..2e83bbf643fe 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,8 +1,8 @@
 # This file defines our primary CI workflow that runs on pull requests
 # and also on pushes to special branches (auto, try).
 #
-# The actual definition of the executed jobs is calculated by a Python
-# script located at src/ci/github-actions/ci.py, which
+# The actual definition of the executed jobs is calculated by the
+# `src/ci/citool` crate, which
 # uses job definition data from src/ci/github-actions/jobs.yml.
 # You should primarily modify the `jobs.yml` file if you want to modify
 # what jobs are executed in CI.
@@ -53,26 +53,37 @@ jobs:
     steps:
       - name: Checkout the source code
         uses: actions/checkout@v4
+      # Cache citool to make its build faster, as it's in the critical path.
+      # The rust-cache doesn't bleed into the main `job`, so it should not affect any other
+      # Rust compilation.
+      - name: Cache citool
+        uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
+        with:
+          workspaces: src/ci/citool
       - name: Calculate the CI job matrix
         env:
           COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
-        run: python3 src/ci/github-actions/ci.py calculate-job-matrix >> $GITHUB_OUTPUT
+        run: |
+          cd src/ci/citool
+          CARGO_INCREMENTAL=0 cargo test
+          CARGO_INCREMENTAL=0 cargo run calculate-job-matrix >> $GITHUB_OUTPUT
         id: jobs
   job:
     name: ${{ matrix.full_name }}
     needs: [ calculate_matrix ]
     runs-on: "${{ matrix.os }}"
-    defaults:
-      run:
-        shell: ${{ contains(matrix.os, 'windows') && 'msys2 {0}' || 'bash' }}
     timeout-minutes: 360
     env:
       CI_JOB_NAME: ${{ matrix.name }}
+      CI_JOB_DOC_URL: ${{ matrix.doc_url }}
+      GITHUB_WORKFLOW_RUN_ID: ${{ github.run_id }}
+      GITHUB_REPOSITORY: ${{ github.repository }}
       CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
       # commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs.
       HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
       DOCKER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
       SCCACHE_BUCKET: rust-lang-ci-sccache2
+      SCCACHE_REGION: us-west-1
       CACHE_DOMAIN: ci-caches.rust-lang.org
     continue-on-error: ${{ matrix.continue_on_error || false }}
     strategy:
@@ -80,22 +91,6 @@ jobs:
         # Check the `calculate_matrix` job to see how is the matrix defined.
         include: ${{ fromJSON(needs.calculate_matrix.outputs.jobs) }}
     steps:
-      - if: contains(matrix.os, 'windows')
-        uses: msys2/setup-msys2@v2.22.0
-        with:
-          # i686 jobs use mingw32. x86_64 and cross-compile jobs use mingw64.
-          msystem: ${{ contains(matrix.name, 'i686') && 'mingw32' || 'mingw64' }}
-          # don't try to download updates for already installed packages
-          update: false
-          # don't try to use the msys that comes built-in to the github runner,
-          # so we can control what is installed (i.e. not python)
-          release: true
-          # Inherit the full path from the Windows environment, with MSYS2's */bin/
-          # dirs placed in front. This lets us run Windows-native Python etc.
-          path-type: inherit
-          install: >
-            make
-
       - name: disable git crlf conversion
         run: git config --global core.autocrlf false
 
@@ -109,7 +104,7 @@ jobs:
       # intensive jobs to run on free runners, which however also have
       # less disk space.
       - name: free up disk space
-        uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
+        run: src/ci/scripts/free-disk-space.sh
         if: matrix.free_disk
 
       # Rust Log Analyzer can't currently detect the PR number of a GitHub
@@ -192,9 +187,33 @@ jobs:
       - name: ensure the stable version number is correct
         run: src/ci/scripts/verify-stable-version-number.sh
 
+      # Show the environment just before we run the build
+      # This makes it easier to diagnose problems with the above install scripts.
+      - name: show the current environment
+        run: src/ci/scripts/dump-environment.sh
+
+      # Pre-build citool before the following step uninstalls rustup
+      # Build it into the build directory, to avoid modifying sources
+      - name: build citool
+        run: |
+          cd src/ci/citool
+          CARGO_INCREMENTAL=0 CARGO_TARGET_DIR=../../../build/citool cargo build
+
       - name: run the build
-        # Redirect stderr to stdout to avoid reordering the two streams in the GHA logs.
-        run: src/ci/scripts/run-build-from-ci.sh 2>&1
+        run: |
+          set +e
+          # Redirect stderr to stdout to avoid reordering the two streams in the GHA logs.
+          src/ci/scripts/run-build-from-ci.sh 2>&1
+          STATUS=$?
+          set -e
+
+          if [[ "$STATUS" -ne 0 && -n "$CI_JOB_DOC_URL" ]]; then
+            echo "****************************************************************************"
+            echo "To find more information about this job, visit the following URL:"
+            echo "$CI_JOB_DOC_URL"
+            echo "****************************************************************************"
+          fi
+          exit ${STATUS}
         env:
           AWS_ACCESS_KEY_ID: ${{ env.CACHES_AWS_ACCESS_KEY_ID }}
           AWS_SECRET_ACCESS_KEY: ${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}
@@ -228,16 +247,37 @@ jobs:
         # erroring about invalid credentials instead.
         if: github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1'
 
+      - name: postprocess metrics into the summary
+        # This step is not critical, and if some I/O problem happens, we don't want
+        # to cancel the build.
+        continue-on-error: true
+        run: |
+          if [ -f build/metrics.json ]; then
+            METRICS=build/metrics.json
+          elif [ -f obj/build/metrics.json ]; then
+            METRICS=obj/build/metrics.json
+          else
+            echo "No metrics.json found"
+            exit 0
+          fi
+
+          # Get closest bors merge commit
+          PARENT_COMMIT=`git rev-list --author='bors <bors@rust-lang.org>' -n1 --first-parent HEAD^1`
+
+          ./build/citool/debug/citool postprocess-metrics \
+              --job-name ${CI_JOB_NAME} \
+              --parent ${PARENT_COMMIT} \
+              ${METRICS} >> ${GITHUB_STEP_SUMMARY}
+
       - name: upload job metrics to DataDog
+        # This step is not critical, and if some I/O problem happens, we don't want
+        # to cancel the build.
+        continue-on-error: true
         if: needs.calculate_matrix.outputs.run_type != 'pr'
         env:
-          DATADOG_SITE: datadoghq.com
           DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
           DD_GITHUB_JOB_NAME: ${{ matrix.full_name }}
-        run: |
-          cd src/ci
-          npm ci
-          python3 scripts/upload-build-metrics.py ../../build/cpu-usage.csv
+        run: ./build/citool/debug/citool upload-build-metrics build/cpu-usage.csv
 
   # This job isused to tell bors the final status of the build, as there is no practical way to detect
   # when a workflow is successful listening to webhooks only in our current bors implementation (homu).
diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml
index 052c9ae72b87..c2c0c11f0083 100644
--- a/.github/workflows/ghcr.yml
+++ b/.github/workflows/ghcr.yml
@@ -5,6 +5,9 @@
 # Docker Hub has a rate limit, while ghcr.io doesn't.
 # Those images are pushed to ghcr.io by this job.
 #
+# While Docker Hub rate limit *shouldn't* be an issue on GitHub Actions,
+# it certainly is for AWS codebuild.
+#
 # Note that authenticating to DockerHub or other registries isn't possible
 # for PR jobs, because forks can't access secrets.
 # That's why we use ghcr.io: it has no rate limit and it doesn't require authentication.
@@ -54,6 +57,10 @@ jobs:
             "ubuntu:22.04"
             # Mirrored because used by all linux CI jobs, including mingw-check-tidy
             "moby/buildkit:buildx-stable-1"
+            # Mirrored because used when CI is running inside a Docker container
+            "alpine:3.4"
+            # Mirrored because used by dist-x86_64-linux
+            "centos:7"
           )
 
           # Mirror each image from DockerHub to ghcr.io
diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml
new file mode 100644
index 000000000000..94553608a2f4
--- /dev/null
+++ b/.github/workflows/post-merge.yml
@@ -0,0 +1,46 @@
+# Workflow that runs after a merge to master, analyses changes in test executions
+# and posts the result to the merged PR.
+
+name: Post merge analysis
+
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+  analysis:
+    runs-on: ubuntu-24.04
+    if: github.repository == 'rust-lang/rust'
+    permissions:
+      pull-requests: write
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          # Make sure that we have enough commits to find the parent merge commit.
+          # Since all merges should be through merge commits, fetching two commits
+          # should be enough to get the parent bors merge commit.
+          fetch-depth: 2
+      - name: Perform analysis and send PR
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: |
+          # Get closest bors merge commit
+          PARENT_COMMIT=`git rev-list --author='bors <bors@rust-lang.org>' -n1 --first-parent HEAD^1`
+          echo "Parent: ${PARENT_COMMIT}"
+
+          # Find PR for the current commit
+          HEAD_PR=`gh pr list --search "${{ github.sha }}" --state merged --json number --jq '.[0].number'`
+          echo "HEAD: ${{ github.sha }} (#${HEAD_PR})"
+
+          cd src/ci/citool
+
+          printf "<details>\n<summary>What is this?</summary>\n" >> output.log
+          printf "This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.\n" >> output.log
+          printf "</details>\n\n" >> output.log
+
+          cargo run --release post-merge-report ${PARENT_COMMIT} ${{ github.sha }} >> output.log
+
+          cat output.log
+
+          gh pr comment ${HEAD_PR} -F output.log
diff --git a/.gitignore b/.gitignore
index f84a3704ca90..a549b6e6e56c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
 # This file should only ignore things that are generated during a `x.py` build,
 # generated by common IDEs, and optional files controlled by the user that
-# affect the build (such as config.toml).
+# affect the build (such as bootstrap.toml).
 # In particular, things like `mir_dump` should not be listed here; they are only
 # created during manual debugging and many people like to clean up instead of
 # having git ignore such leftovers. You can use `.git/info/exclude` to
@@ -34,6 +34,7 @@ Session.vim
 !/tests/run-make/thumb-none-qemu/example/.cargo
 
 ## Configuration
+/bootstrap.toml
 /config.toml
 /Makefile
 config.mk
@@ -53,7 +54,10 @@ no_llvm_build
 /target
 /library/target
 /src/bootstrap/target
+/src/ci/citool/target
 /src/tools/x/target
+# Created by `x vendor`
+/vendor
 # Created by default with `src/ci/docker/run.sh`
 /obj/
 # Created by nix dev shell / .envrc
@@ -83,6 +87,7 @@ __pycache__/
 node_modules
 package-lock.json
 package.json
+/src/doc/rustc-dev-guide/mermaid.min.js
 
 ## Rustdoc GUI tests
 tests/rustdoc-gui/src/**.lock
diff --git a/.gitmodules b/.gitmodules
index f9bd42edab3f..d09d81ccadcb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -29,7 +29,7 @@
 [submodule "src/llvm-project"]
 	path = src/llvm-project
 	url = https://github.com/rust-lang/llvm-project.git
-	branch = rustc/19.1-2024-12-03
+	branch = rustc/20.1-2025-02-13
 	shallow = true
 [submodule "src/doc/embedded-book"]
 	path = src/doc/embedded-book
@@ -45,7 +45,7 @@
 	shallow = true
 [submodule "src/tools/enzyme"]
 	path = src/tools/enzyme
-	url = https://github.com/EnzymeAD/Enzyme.git
+	url = https://github.com/rust-lang/Enzyme.git
 	shallow = true
 [submodule "src/gcc"]
 	path = src/gcc
diff --git a/.ignore b/.ignore
index 40d1513978fc..b0c640fee3da 100644
--- a/.ignore
+++ b/.ignore
@@ -1,2 +1,3 @@
-# Make vscode *not* count `config.toml` as ignored, so it is included in search
+# Make vscode *not* count `bootstrap.toml` and `config.toml` as ignored, so it is included in search
+!/bootstrap.toml
 !/config.toml
diff --git a/.mailmap b/.mailmap
index eb6a9fcabca9..c3ce111bfe3b 100644
--- a/.mailmap
+++ b/.mailmap
@@ -276,7 +276,7 @@ Jacob Greenfield <xales@naveria.com>
 Jacob Pratt <jacob@jhpratt.dev> <the.z.cuber@gmail.com>
 Jacob Pratt <jacob@jhpratt.dev> <jacopratt@tesla.com>
 Jake Goulding <jake.goulding@integer32.com>
-Jake Goulding <jake.goulding@integer32.com> <jake.goulding@gmail.com> 
+Jake Goulding <jake.goulding@integer32.com> <jake.goulding@gmail.com>
 Jake Goulding <jake.goulding@integer32.com> <shepmaster@mac.com>
 Jake Vossen <jake@vossen.dev>
 Jakob Degen <jakob.e.degen@gmail.com> <jakob@degen.com>
@@ -292,6 +292,10 @@ James Hinshelwood <jameshinshelwood1@gmail.com> <james.hinshelwood@bigpayme.com>
 James Miller <bladeon@gmail.com> <james@aatch.net>
 James Perry <james.austin.perry@gmail.com>
 James Sanderson <zofrex@gmail.com>
+Jamie Hill-Daniel <jamie@hill-daniel.co.uk> <clubby789@gmail.com>
+Jana Dönszelmann <jana@donsz.nl>
+Jana Dönszelmann <jana@donsz.nl> <jonathan@donsz.nl>
+Jana Dönszelmann <jana@donsz.nl> <jonabent@gmail.com>
 Jan-Erik Rediger <janerik@fnordig.de> <badboy@archlinux.us>
 Jaro Fietz <jaro.fietz@gmx.de>
 Jason Fager <jfager@gmail.com>
@@ -405,10 +409,13 @@ Luqman Aden <me@luqman.ca> <laden@mozilla.com>
 Luqman Aden <me@luqman.ca> <rust@luqman.ca>
 Lzu Tao <taolzu@gmail.com>
 Maik Klein <maikklein@googlemail.com>
+Maja Kądziołka <maya@compilercrim.es> <github@compilercrim.es>
+Maja Kądziołka <maya@compilercrim.es> <kuba@kadziolka.net>
 Malo Jaffré <jaffre.malo@gmail.com>
 Manish Goregaokar <manishsmail@gmail.com>
 Mara Bos <m-ou.se@m-ou.se>
 Marcell Pardavi <marcell.pardavi@gmail.com>
+Marco Ieni <11428655+MarcoIeni@users.noreply.github.com>
 Marcus Klaas de Vries <mail@marcusklaas.nl>
 Margaret Meyerhofer <mmeyerho@andrew.cmu.edu> <mmeyerho@andrew>
 Mark Mansi <markm@cs.wisc.edu>
@@ -562,6 +569,9 @@ Robert Habermeier <rphmeier@gmail.com>
 Robert Millar <robert.millar@cantab.net>
 Roc Yu <rocyu@protonmail.com>
 Rohit Joshi <rohitjoshi@users.noreply.github.com> Rohit Joshi <rohit.joshi@capitalone.com>
+Ross Smyth <18294397+RossSmyth@users.noreply.github.com>
+Ross Smyth <18294397+RossSmyth@users.noreply.github.com> <crs2017@gmail.com>
+Ross Smyth <18294397+RossSmyth@users.noreply.github.com> <rsmyth@electrocraft.com>
 Roxane Fruytier <roxane.fruytier@hotmail.com>
 Rui <xiongmao86dev@sina.com>
 Russell Johnston <rpjohnst@gmail.com>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a5ddff595f5d..e155e253784a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -18,7 +18,7 @@ For submodules, changes need to be made against the repository corresponding the
 submodule, and not the main `rust-lang/rust` repository.
 
 For subtrees, prefer sending a PR against the subtree's repository if it does
-not need to be made against the main `rust-lang/rust` repostory (e.g. a
+not need to be made against the main `rust-lang/rust` repository (e.g. a
 rustc-dev-guide change that does not accompany a compiler change).
 
 ## About the [rustc-dev-guide]
diff --git a/Cargo.lock b/Cargo.lock
index 1d48617d8b5e..684dfbca6f15 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -34,18 +34,6 @@ dependencies = [
  "cpufeatures",
 ]
 
-[[package]]
-name = "ahash"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
-dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
 [[package]]
 name = "aho-corasick"
 version = "1.1.3"
@@ -61,19 +49,6 @@ version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
 
-[[package]]
-name = "ammonia"
-version = "4.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459"
-dependencies = [
- "html5ever",
- "maplit",
- "once_cell",
- "tendril",
- "url",
-]
-
 [[package]]
 name = "android-tzdata"
 version = "0.1.1"
@@ -183,9 +158,12 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.95"
+version = "1.0.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+dependencies = [
+ "backtrace",
+]
 
 [[package]]
 name = "ar_archive_writer"
@@ -208,6 +186,48 @@ version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
+[[package]]
+name = "askama"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a4e46abb203e00ef226442d452769233142bbfdd79c3941e84c8e61c4112543"
+dependencies = [
+ "askama_derive",
+ "itoa",
+ "percent-encoding",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "askama_derive"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54398906821fd32c728135f7b351f0c7494ab95ae421d41b6f5a020e158f28a6"
+dependencies = [
+ "askama_parser",
+ "basic-toml",
+ "memchr",
+ "proc-macro2",
+ "quote",
+ "rustc-hash 2.1.1",
+ "serde",
+ "serde_derive",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "askama_parser"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
+dependencies = [
+ "memchr",
+ "serde",
+ "serde_derive",
+ "winnow 0.7.4",
+]
+
 [[package]]
 name = "autocfg"
 version = "1.4.0"
@@ -237,9 +257,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
 
 [[package]]
 name = "basic-toml"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
+checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
 dependencies = [
  "serde",
 ]
@@ -255,15 +275,15 @@ dependencies = [
 
 [[package]]
 name = "bitflags"
-version = "2.8.0"
+version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
 
 [[package]]
 name = "blake3"
-version = "1.5.5"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e"
+checksum = "389a099b34312839e16420d499a9cad9650541715937ffbdd40d36f49e77eeb3"
 dependencies = [
  "arrayref",
  "arrayvec",
@@ -330,9 +350,9 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.16.0"
+version = "3.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
 
 [[package]]
 name = "bytecount"
@@ -340,17 +360,11 @@ version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
 
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
 [[package]]
 name = "bytes"
-version = "1.9.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
 
 [[package]]
 name = "camino"
@@ -365,10 +379,10 @@ dependencies = [
 name = "cargo-miri"
 version = "0.1.0"
 dependencies = [
- "cargo_metadata 0.18.1",
+ "cargo_metadata 0.19.2",
  "directories",
  "rustc-build-sysroot",
- "rustc_tools_util",
+ "rustc_tools_util 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc_version",
  "serde",
  "serde_json",
@@ -399,16 +413,16 @@ dependencies = [
 
 [[package]]
 name = "cargo_metadata"
-version = "0.19.1"
+version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924"
+checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
 dependencies = [
  "camino",
  "cargo-platform",
  "semver",
  "serde",
  "serde_json",
- "thiserror 2.0.11",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
@@ -417,9 +431,9 @@ version = "0.1.0"
 
 [[package]]
 name = "cc"
-version = "1.2.7"
+version = "1.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
+checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
 dependencies = [
  "shlex",
 ]
@@ -438,22 +452,22 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
 [[package]]
 name = "chrono"
-version = "0.4.39"
+version = "0.4.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
+checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
  "num-traits",
  "serde",
- "windows-targets 0.52.6",
+ "windows-link",
 ]
 
 [[package]]
 name = "chrono-tz"
-version = "0.10.1"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f"
+checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3"
 dependencies = [
  "chrono",
  "chrono-tz-build",
@@ -462,9 +476,9 @@ dependencies = [
 
 [[package]]
 name = "chrono-tz-build"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7"
+checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402"
 dependencies = [
  "parse-zoneinfo",
  "phf_codegen",
@@ -482,9 +496,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.26"
+version = "4.5.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
+checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -502,36 +516,26 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.26"
+version = "4.5.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
+checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
 dependencies = [
  "anstream",
  "anstyle",
  "clap_lex",
  "strsim",
- "terminal_size",
-]
-
-[[package]]
-name = "clap_complete"
-version = "4.5.42"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0"
-dependencies = [
- "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.5.24"
+version = "4.5.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
+checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
 dependencies = [
  "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -542,7 +546,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
 
 [[package]]
 name = "clippy"
-version = "0.1.86"
+version = "0.1.87"
 dependencies = [
  "anstream",
  "cargo_metadata 0.18.1",
@@ -559,10 +563,10 @@ dependencies = [
  "quote",
  "regex",
  "rinja",
- "rustc_tools_util",
+ "rustc_tools_util 0.4.2",
  "serde",
  "serde_json",
- "syn 2.0.96",
+ "syn 2.0.100",
  "tempfile",
  "termize",
  "tokio",
@@ -573,7 +577,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_config"
-version = "0.1.86"
+version = "0.1.87"
 dependencies = [
  "clippy_utils",
  "itertools",
@@ -598,7 +602,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_lints"
-version = "0.1.86"
+version = "0.1.87"
 dependencies = [
  "arrayvec",
  "cargo_metadata 0.18.1",
@@ -621,7 +625,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_utils"
-version = "0.1.86"
+version = "0.1.87"
 dependencies = [
  "arrayvec",
  "itertools",
@@ -672,7 +676,7 @@ dependencies = [
  "nom",
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -714,8 +718,8 @@ name = "compiletest"
 version = "0.0.0"
 dependencies = [
  "anstyle-svg",
- "anyhow",
  "build_helper",
+ "camino",
  "colored",
  "diff",
  "getopts",
@@ -734,14 +738,14 @@ dependencies = [
  "tracing-subscriber",
  "unified-diff",
  "walkdir",
- "windows",
+ "windows 0.59.0",
 ]
 
 [[package]]
 name = "console"
-version = "0.15.10"
+version = "0.15.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
+checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
 dependencies = [
  "encode_unicode",
  "libc",
@@ -776,9 +780,9 @@ dependencies = [
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.16"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
 dependencies = [
  "libc",
 ]
@@ -838,9 +842,9 @@ dependencies = [
 
 [[package]]
 name = "ctrlc"
-version = "3.4.5"
+version = "3.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
+checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c"
 dependencies = [
  "nix",
  "windows-sys 0.59.0",
@@ -863,9 +867,9 @@ dependencies = [
 
 [[package]]
 name = "curl-sys"
-version = "0.4.78+curl-8.11.0"
+version = "0.4.80+curl-8.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf"
+checksum = "55f7df2eac63200c3ab25bde3b2268ef2ee56af3d238e76d61f01c3c49bff734"
 dependencies = [
  "cc",
  "libc",
@@ -878,9 +882,9 @@ dependencies = [
 
 [[package]]
 name = "darling"
-version = "0.20.10"
+version = "0.20.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
+checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
 dependencies = [
  "darling_core",
  "darling_macro",
@@ -888,27 +892,27 @@ dependencies = [
 
 [[package]]
 name = "darling_core"
-version = "0.20.10"
+version = "0.20.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
+checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
 dependencies = [
  "fnv",
  "ident_case",
  "proc-macro2",
  "quote",
  "strsim",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "darling_macro"
-version = "0.20.10"
+version = "0.20.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
+checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -928,15 +932,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "deranged"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
-dependencies = [
- "powerfmt",
-]
-
 [[package]]
 name = "derive-where"
 version = "1.2.7"
@@ -945,7 +940,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -966,7 +961,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -976,7 +971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
 dependencies = [
  "derive_builder_core",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -988,7 +983,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -1009,11 +1004,11 @@ dependencies = [
 
 [[package]]
 name = "directories"
-version = "5.0.1"
+version = "6.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
+checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
 dependencies = [
- "dirs-sys",
+ "dirs-sys 0.5.0",
 ]
 
 [[package]]
@@ -1022,7 +1017,7 @@ version = "5.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
 dependencies = [
- "dirs-sys",
+ "dirs-sys 0.4.1",
 ]
 
 [[package]]
@@ -1043,10 +1038,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
 dependencies = [
  "libc",
  "option-ext",
- "redox_users",
+ "redox_users 0.4.6",
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users 0.5.0",
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "dirs-sys-next"
 version = "0.1.2"
@@ -1054,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
 dependencies = [
  "libc",
- "redox_users",
+ "redox_users 0.4.6",
  "winapi",
 ]
 
@@ -1066,38 +1073,26 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "dissimilar"
-version = "1.0.9"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
+checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
 
 [[package]]
 name = "either"
-version = "1.13.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
-
-[[package]]
-name = "elasticlunr-rs"
-version = "3.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41e83863a500656dfa214fee6682de9c5b9f03de6860fec531235ed2ae9f6571"
-dependencies = [
- "regex",
- "serde",
- "serde_derive",
- "serde_json",
-]
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 
 [[package]]
 name = "elsa"
-version = "1.7.1"
+version = "1.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "848fe615fbb0a74d9ae68dcaa510106d32e37d9416207bbea4bd008bd89c47ed"
+checksum = "9abf33c656a7256451ebb7d0082c5a471820c31269e49d807c538c252352186e"
 dependencies = [
  "stable_deref_trait",
 ]
@@ -1129,40 +1124,33 @@ dependencies = [
 
 [[package]]
 name = "env_logger"
-version = "0.11.6"
+version = "0.11.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
+checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
 dependencies = [
  "anstream",
  "anstyle",
  "env_filter",
- "humantime",
+ "jiff",
  "log",
 ]
 
 [[package]]
 name = "equivalent"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
 
 [[package]]
 name = "errno"
-version = "0.3.10"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
 dependencies = [
  "libc",
  "windows-sys 0.59.0",
 ]
 
-[[package]]
-name = "error_index_generator"
-version = "0.0.0"
-dependencies = [
- "mdbook",
-]
-
 [[package]]
 name = "expect-test"
 version = "1.5.1"
@@ -1196,13 +1184,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
 [[package]]
-name = "field-offset"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
+name = "features-status-dump"
+version = "0.1.0"
 dependencies = [
- "memoffset",
- "rustc_version",
+ "anyhow",
+ "clap",
+ "serde",
+ "serde_json",
+ "tidy",
 ]
 
 [[package]]
@@ -1219,12 +1208,12 @@ dependencies = [
 
 [[package]]
 name = "flate2"
-version = "1.0.35"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
 dependencies = [
  "crc32fast",
- "miniz_oxide 0.8.3",
+ "miniz_oxide 0.8.8",
 ]
 
 [[package]]
@@ -1269,9 +1258,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "foldhash"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
 
 [[package]]
 name = "form_urlencoded"
@@ -1363,7 +1352,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -1401,8 +1390,8 @@ name = "generate-copyright"
 version = "0.1.0"
 dependencies = [
  "anyhow",
+ "askama",
  "cargo_metadata 0.18.1",
- "rinja",
  "serde",
  "serde_json",
  "thiserror 1.0.69",
@@ -1442,25 +1431,26 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
 ]
 
 [[package]]
-name = "gimli"
-version = "0.28.1"
+name = "getrandom"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+]
 
 [[package]]
 name = "gimli"
-version = "0.30.0"
+version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9"
-dependencies = [
- "fallible-iterator",
- "indexmap",
- "stable_deref_trait",
-]
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
 name = "gimli"
@@ -1481,9 +1471,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
 
 [[package]]
 name = "globset"
-version = "0.4.15"
+version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
+checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
 dependencies = [
  "aho-corasick",
  "bstr",
@@ -1501,38 +1491,14 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "handlebars"
-version = "6.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9"
-dependencies = [
- "derive_builder",
- "log",
- "num-order",
- "pest",
- "pest_derive",
- "serde",
- "serde_json",
- "thiserror 2.0.11",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash",
- "allocator-api2",
-]
-
 [[package]]
 name = "hashbrown"
 version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
 dependencies = [
+ "allocator-api2",
+ "equivalent",
  "foldhash",
  "serde",
 ]
@@ -1589,16 +1555,14 @@ dependencies = [
 
 [[package]]
 name = "html5ever"
-version = "0.27.0"
+version = "0.29.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
+checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c"
 dependencies = [
  "log",
  "mac",
  "markup5ever",
- "proc-macro2",
- "quote",
- "syn 2.0.96",
+ "match_token",
 ]
 
 [[package]]
@@ -1612,22 +1576,23 @@ dependencies = [
 
 [[package]]
 name = "humantime"
-version = "2.1.0"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.61"
+version = "0.1.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
  "iana-time-zone-haiku",
  "js-sys",
+ "log",
  "wasm-bindgen",
- "windows-core 0.52.0",
+ "windows-core 0.61.0",
 ]
 
 [[package]]
@@ -1667,9 +1632,9 @@ dependencies = [
 
 [[package]]
 name = "icu_list_data"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f"
+checksum = "52b1a7fbdbf3958f1be8354cb59ac73f165b7b7082d447ff2090355c9a069120"
 
 [[package]]
 name = "icu_locid"
@@ -1700,9 +1665,9 @@ dependencies = [
 
 [[package]]
 name = "icu_locid_transform_data"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
 
 [[package]]
 name = "icu_normalizer"
@@ -1724,9 +1689,9 @@ dependencies = [
 
 [[package]]
 name = "icu_normalizer_data"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
 
 [[package]]
 name = "icu_properties"
@@ -1745,9 +1710,9 @@ dependencies = [
 
 [[package]]
 name = "icu_properties_data"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
 
 [[package]]
 name = "icu_provider"
@@ -1787,7 +1752,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -1853,21 +1818,20 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
 
 [[package]]
 name = "indexmap"
-version = "2.7.0"
+version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
 dependencies = [
  "equivalent",
- "hashbrown 0.15.2",
- "rustc-rayon",
+ "hashbrown",
  "serde",
 ]
 
 [[package]]
 name = "indicatif"
-version = "0.17.9"
+version = "0.17.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281"
+checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
 dependencies = [
  "console",
  "number_prefix",
@@ -1884,9 +1848,9 @@ checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
 
 [[package]]
 name = "inout"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
 dependencies = [
  "generic-array",
 ]
@@ -1940,16 +1904,41 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.14"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jiff"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260"
+dependencies = [
+ "jiff-static",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
 
 [[package]]
 name = "jobserver"
-version = "0.1.32"
+version = "0.1.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
 dependencies = [
+ "getrandom 0.3.2",
  "libc",
 ]
 
@@ -1969,7 +1958,7 @@ version = "0.1.0"
 dependencies = [
  "fs-err",
  "getopts",
- "jsonpath_lib",
+ "jsonpath-rust",
  "regex",
  "serde_json",
  "shlex",
@@ -1982,21 +1971,23 @@ dependencies = [
  "anyhow",
  "clap",
  "fs-err",
- "rustc-hash 2.1.0",
+ "rustc-hash 2.1.1",
  "rustdoc-json-types",
  "serde",
  "serde_json",
 ]
 
 [[package]]
-name = "jsonpath_lib"
-version = "0.3.0"
+name = "jsonpath-rust"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f"
+checksum = "9b0231bb404a6cd6c8f0ab41b907049063a089fc02aa7636cc5cd9a4d87364c9"
 dependencies = [
- "log",
- "serde",
+ "pest",
+ "pest_derive",
+ "regex",
  "serde_json",
+ "thiserror 2.0.12",
 ]
 
 [[package]]
@@ -2011,6 +2002,12 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
 
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
 [[package]]
 name = "levenshtein"
 version = "1.0.5"
@@ -2019,15 +2016,15 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
 
 [[package]]
 name = "lexopt"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"
+checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
 
 [[package]]
 name = "libc"
-version = "0.2.169"
+version = "0.2.171"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
 
 [[package]]
 name = "libdbus-sys"
@@ -2041,9 +2038,9 @@ dependencies = [
 
 [[package]]
 name = "libffi"
-version = "3.2.0"
+version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2"
+checksum = "4a9434b6fc77375fb624698d5f8c49d7e80b10d59eb1219afda27d1f824d4074"
 dependencies = [
  "libc",
  "libffi-sys",
@@ -2051,9 +2048,9 @@ dependencies = [
 
 [[package]]
 name = "libffi-sys"
-version = "2.3.0"
+version = "3.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c"
+checksum = "ead36a2496acfc8edd6cc32352110e9478ac5b9b5f5b9856ebd3d28019addb84"
 dependencies = [
  "cc",
 ]
@@ -2087,9 +2084,9 @@ dependencies = [
 
 [[package]]
 name = "libz-sys"
-version = "1.1.21"
+version = "1.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa"
+checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d"
 dependencies = [
  "cc",
  "libc",
@@ -2116,15 +2113,15 @@ dependencies = [
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.15"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
 
 [[package]]
 name = "litemap"
-version = "0.7.4"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
 
 [[package]]
 name = "lld-wrapper"
@@ -2153,9 +2150,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.25"
+version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
 [[package]]
 name = "lzma-sys"
@@ -2174,17 +2171,11 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
 
-[[package]]
-name = "maplit"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
-
 [[package]]
 name = "markup5ever"
-version = "0.12.1"
+version = "0.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
+checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18"
 dependencies = [
  "log",
  "phf",
@@ -2194,6 +2185,17 @@ dependencies = [
  "tendril",
 ]
 
+[[package]]
+name = "match_token"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
 [[package]]
 name = "matchers"
 version = "0.1.0"
@@ -2213,39 +2215,11 @@ dependencies = [
  "digest",
 ]
 
-[[package]]
-name = "mdbook"
-version = "0.4.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe1f98b8d66e537d2f0ba06e7dec4f44001deec539a2d18bfc102d6a86189148"
-dependencies = [
- "ammonia",
- "anyhow",
- "chrono",
- "clap",
- "clap_complete",
- "elasticlunr-rs",
- "env_logger",
- "handlebars",
- "log",
- "memchr",
- "once_cell",
- "opener",
- "pulldown-cmark 0.10.3",
- "regex",
- "serde",
- "serde_json",
- "shlex",
- "tempfile",
- "toml 0.5.11",
- "topological-sort",
-]
-
 [[package]]
 name = "measureme"
-version = "11.0.1"
+version = "12.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfa4a40f09af7aa6faef38285402a78847d0d72bf8827006cd2a332e1e6e4a8d"
+checksum = "570a507d8948a66a97f42cbbaf8a6bb9516a51017d4ee949502ad7a10a864395"
 dependencies = [
  "log",
  "memmap2",
@@ -2270,15 +2244,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "memoffset"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
-dependencies = [
- "autocfg",
-]
-
 [[package]]
 name = "mime"
 version = "0.3.17"
@@ -2297,9 +2262,9 @@ dependencies = [
 
 [[package]]
 name = "minifier"
-version = "0.3.2"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd559bbf5d350ac7f2c1cf92ed71a869b847a92bce0c1318b47932a5b5f65cdd"
+checksum = "9bfdc64e2f805f3d12965f10522000bae36e88d2cfea44112331f467d4f4bf68"
 
 [[package]]
 name = "minimal-lexical"
@@ -2318,9 +2283,9 @@ dependencies = [
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.3"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
 dependencies = [
  "adler2",
 ]
@@ -2339,23 +2304,24 @@ name = "miri"
 version = "0.1.0"
 dependencies = [
  "aes",
+ "bitflags",
  "chrono",
  "chrono-tz",
  "colored",
  "directories",
- "getrandom",
+ "getrandom 0.3.2",
  "libc",
  "libffi",
  "libloading",
  "measureme",
- "rand",
+ "rand 0.9.0",
  "regex",
  "rustc_version",
  "smallvec",
  "tempfile",
  "tikv-jemalloc-sys",
  "ui_test",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -2451,12 +2417,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "num-conv"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
-
 [[package]]
 name = "num-integer"
 version = "0.1.46"
@@ -2477,21 +2437,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "num-modular"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
-
-[[package]]
-name = "num-order"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6"
-dependencies = [
- "num-modular",
-]
-
 [[package]]
 name = "num-rational"
 version = "0.4.2"
@@ -2545,11 +2490,11 @@ checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
 dependencies = [
  "crc32fast",
  "flate2",
- "hashbrown 0.15.2",
+ "hashbrown",
  "indexmap",
  "memchr",
  "ruzstd",
- "wasmparser 0.222.0",
+ "wasmparser 0.222.1",
 ]
 
 [[package]]
@@ -2563,9 +2508,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.20.2"
+version = "1.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
 [[package]]
 name = "opener"
@@ -2581,15 +2526,15 @@ dependencies = [
 
 [[package]]
 name = "openssl-probe"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.104"
+version = "0.9.107"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
+checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07"
 dependencies = [
  "cc",
  "libc",
@@ -2627,6 +2572,16 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
 
+[[package]]
+name = "os_pipe"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "overload"
 version = "0.1.1"
@@ -2714,20 +2669,20 @@ dependencies = [
 
 [[package]]
 name = "pest"
-version = "2.7.15"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
+checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
 dependencies = [
  "memchr",
- "thiserror 2.0.11",
+ "thiserror 2.0.12",
  "ucd-trie",
 ]
 
 [[package]]
 name = "pest_derive"
-version = "2.7.15"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
+checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
 dependencies = [
  "pest",
  "pest_generator",
@@ -2735,22 +2690,22 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.7.15"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
+checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
 dependencies = [
  "pest",
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "pest_meta"
-version = "2.7.15"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
+checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
 dependencies = [
  "once_cell",
  "pest",
@@ -2763,7 +2718,7 @@ version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
 dependencies = [
- "phf_shared 0.11.3",
+ "phf_shared",
 ]
 
 [[package]]
@@ -2772,18 +2727,8 @@ version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
 dependencies = [
- "phf_generator 0.11.3",
- "phf_shared 0.11.3",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
-dependencies = [
- "phf_shared 0.10.0",
- "rand",
+ "phf_generator",
+ "phf_shared",
 ]
 
 [[package]]
@@ -2792,17 +2737,8 @@ version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
 dependencies = [
- "phf_shared 0.11.3",
- "rand",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
-dependencies = [
- "siphasher 0.3.11",
+ "phf_shared",
+ "rand 0.8.5",
 ]
 
 [[package]]
@@ -2811,7 +2747,7 @@ version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
 dependencies = [
- "siphasher 1.0.1",
+ "siphasher",
 ]
 
 [[package]]
@@ -2828,9 +2764,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 [[package]]
 name = "pkg-config"
-version = "0.3.31"
+version = "0.3.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
 
 [[package]]
 name = "polonius-engine"
@@ -2845,21 +2781,24 @@ dependencies = [
 
 [[package]]
 name = "portable-atomic"
-version = "1.10.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
+checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
 
 [[package]]
-name = "powerfmt"
-version = "0.2.0"
+name = "portable-atomic-util"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.20"
+version = "0.2.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
 dependencies = [
  "zerocopy",
 ]
@@ -2888,18 +2827,18 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.93"
+version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "psm"
-version = "0.1.24"
+version = "0.1.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
+checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88"
 dependencies = [
  "cc",
 ]
@@ -2915,18 +2854,6 @@ dependencies = [
  "unicase",
 ]
 
-[[package]]
-name = "pulldown-cmark"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993"
-dependencies = [
- "bitflags",
- "memchr",
- "pulldown-cmark-escape 0.10.1",
- "unicase",
-]
-
 [[package]]
 name = "pulldown-cmark"
 version = "0.11.3"
@@ -2935,16 +2862,10 @@ checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
 dependencies = [
  "bitflags",
  "memchr",
- "pulldown-cmark-escape 0.11.0",
+ "pulldown-cmark-escape",
  "unicase",
 ]
 
-[[package]]
-name = "pulldown-cmark-escape"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3"
-
 [[package]]
 name = "pulldown-cmark-escape"
 version = "0.11.0"
@@ -2965,13 +2886,19 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
 
 [[package]]
 name = "quote"
-version = "1.0.38"
+version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
 dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
 [[package]]
 name = "rand"
 version = "0.8.5"
@@ -2979,8 +2906,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
- "rand_chacha",
- "rand_core",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.3",
+ "zerocopy",
 ]
 
 [[package]]
@@ -2990,7 +2928,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.3",
 ]
 
 [[package]]
@@ -2999,16 +2947,25 @@ version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.2",
 ]
 
 [[package]]
 name = "rand_xoshiro"
-version = "0.6.0"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
+checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41"
 dependencies = [
- "rand_core",
+ "rand_core 0.9.3",
 ]
 
 [[package]]
@@ -3033,9 +2990,9 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.8"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
+checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
 dependencies = [
  "bitflags",
 ]
@@ -3046,11 +3003,22 @@ version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
 dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
  "libredox",
  "thiserror 1.0.69",
 ]
 
+[[package]]
+name = "redox_users"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
+dependencies = [
+ "getrandom 0.2.15",
+ "libredox",
+ "thiserror 2.0.12",
+]
+
 [[package]]
 name = "regex"
 version = "1.11.1"
@@ -3132,9 +3100,7 @@ version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5"
 dependencies = [
- "humansize",
  "itoa",
- "percent-encoding",
  "rinja_derive",
 ]
 
@@ -3151,9 +3117,9 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rinja_parser",
- "rustc-hash 2.1.0",
+ "rustc-hash 2.1.1",
  "serde",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -3167,13 +3133,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "rls"
-version = "2.0.0"
-dependencies = [
- "serde_json",
-]
-
 [[package]]
 name = "run_make_support"
 version = "0.2.0"
@@ -3183,10 +3142,11 @@ dependencies = [
  "gimli 0.31.1",
  "libc",
  "object 0.36.7",
+ "os_pipe",
  "regex",
  "serde_json",
  "similar",
- "wasmparser 0.219.1",
+ "wasmparser 0.219.2",
 ]
 
 [[package]]
@@ -3215,9 +3175,15 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
 name = "rustc-hash"
-version = "2.1.0"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
+name = "rustc-literal-escaper"
+version = "0.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
+checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
 
 [[package]]
 name = "rustc-main"
@@ -3231,40 +3197,21 @@ dependencies = [
  "tikv-jemalloc-sys",
 ]
 
-[[package]]
-name = "rustc-perf-wrapper"
-version = "0.1.0"
-dependencies = [
- "clap",
-]
-
-[[package]]
-name = "rustc-rayon"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb81aadc8837ca6ecebe0fe1353f15df83b3b3cc2cf7a8afd571bc22aa121710"
-dependencies = [
- "either",
- "rustc-rayon-core",
-]
-
 [[package]]
 name = "rustc-rayon-core"
-version = "0.5.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67668daaf00e359c126f6dcb40d652d89b458a008c8afa727a42a2d20fca0b7f"
+checksum = "2f42932dcd3bcbe484b38a3ccf79b7906fac41c02d408b5b1bac26da3416efdb"
 dependencies = [
- "crossbeam-channel",
  "crossbeam-deque",
  "crossbeam-utils",
- "num_cpus",
 ]
 
 [[package]]
 name = "rustc-stable-hash"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2febf9acc5ee5e99d1ad0afcdbccc02d87aa3f857a1f01f825b80eacf8edfcd1"
+checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08"
 
 [[package]]
 name = "rustc-std-workspace-alloc"
@@ -3283,10 +3230,10 @@ name = "rustc_abi"
 version = "0.0.0"
 dependencies = [
  "bitflags",
- "rand",
+ "rand 0.9.0",
  "rand_xoshiro",
  "rustc_data_structures",
- "rustc_feature",
+ "rustc_hashes",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
@@ -3317,10 +3264,10 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "memchr",
+ "rustc-literal-escaper",
  "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_index",
- "rustc_lexer",
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
@@ -3336,17 +3283,19 @@ dependencies = [
  "rustc_data_structures",
  "rustc_macros",
  "rustc_serialize",
- "rustc_span",
 ]
 
 [[package]]
 name = "rustc_ast_lowering"
 version = "0.0.0"
 dependencies = [
+ "rustc_abi",
  "rustc_ast",
  "rustc_ast_pretty",
+ "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
+ "rustc_feature",
  "rustc_fluent_macro",
  "rustc_hir",
  "rustc_index",
@@ -3365,6 +3314,7 @@ name = "rustc_ast_passes"
 version = "0.0.0"
 dependencies = [
  "itertools",
+ "rustc_abi",
  "rustc_ast",
  "rustc_ast_pretty",
  "rustc_attr_parsing",
@@ -3376,7 +3326,6 @@ dependencies = [
  "rustc_parse",
  "rustc_session",
  "rustc_span",
- "rustc_target",
  "thin-vec",
 ]
 
@@ -3386,7 +3335,6 @@ version = "0.0.0"
 dependencies = [
  "itertools",
  "rustc_ast",
- "rustc_data_structures",
  "rustc_lexer",
  "rustc_span",
  "thin-vec",
@@ -3400,14 +3348,10 @@ dependencies = [
  "rustc_ast",
  "rustc_ast_pretty",
  "rustc_data_structures",
- "rustc_errors",
- "rustc_feature",
- "rustc_fluent_macro",
- "rustc_lexer",
  "rustc_macros",
  "rustc_serialize",
- "rustc_session",
  "rustc_span",
+ "thin-vec",
 ]
 
 [[package]]
@@ -3418,15 +3362,15 @@ dependencies = [
  "rustc_ast",
  "rustc_ast_pretty",
  "rustc_attr_data_structures",
- "rustc_data_structures",
  "rustc_errors",
  "rustc_feature",
  "rustc_fluent_macro",
+ "rustc_hir",
  "rustc_lexer",
  "rustc_macros",
- "rustc_serialize",
  "rustc_session",
  "rustc_span",
+ "thin-vec",
 ]
 
 [[package]]
@@ -3479,6 +3423,7 @@ dependencies = [
  "rustc_expand",
  "rustc_feature",
  "rustc_fluent_macro",
+ "rustc_hir",
  "rustc_index",
  "rustc_lexer",
  "rustc_lint_defs",
@@ -3498,7 +3443,7 @@ name = "rustc_codegen_llvm"
 version = "0.0.0"
 dependencies = [
  "bitflags",
- "gimli 0.30.0",
+ "gimli 0.31.1",
  "itertools",
  "libc",
  "measureme",
@@ -3512,6 +3457,7 @@ dependencies = [
  "rustc_errors",
  "rustc_fluent_macro",
  "rustc_fs_util",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_index",
  "rustc_llvm",
@@ -3537,6 +3483,7 @@ dependencies = [
  "ar_archive_writer",
  "arrayvec",
  "bitflags",
+ "bstr",
  "cc",
  "either",
  "itertools",
@@ -3547,14 +3494,13 @@ dependencies = [
  "rustc_abi",
  "rustc_arena",
  "rustc_ast",
- "rustc_ast_pretty",
  "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
  "rustc_fs_util",
+ "rustc_hashes",
  "rustc_hir",
- "rustc_hir_pretty",
  "rustc_incremental",
  "rustc_index",
  "rustc_macros",
@@ -3567,15 +3513,14 @@ dependencies = [
  "rustc_symbol_mangling",
  "rustc_target",
  "rustc_trait_selection",
- "rustc_type_ir",
  "serde_json",
  "smallvec",
  "tempfile",
  "thin-vec",
  "thorin-dwp",
  "tracing",
- "wasm-encoder 0.219.1",
- "windows",
+ "wasm-encoder 0.219.2",
+ "windows 0.59.0",
 ]
 
 [[package]]
@@ -3600,7 +3545,6 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "rustc_type_ir",
  "tracing",
 ]
 
@@ -3613,6 +3557,7 @@ dependencies = [
  "either",
  "elsa",
  "ena",
+ "hashbrown",
  "indexmap",
  "jobserver",
  "libc",
@@ -3620,11 +3565,12 @@ dependencies = [
  "memmap2",
  "parking_lot",
  "portable-atomic",
- "rustc-hash 2.1.0",
- "rustc-rayon",
+ "rustc-hash 2.1.1",
+ "rustc-rayon-core",
  "rustc-stable-hash",
  "rustc_arena",
  "rustc_graphviz",
+ "rustc_hashes",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
@@ -3633,7 +3579,7 @@ dependencies = [
  "tempfile",
  "thin-vec",
  "tracing",
- "windows",
+ "windows 0.59.0",
 ]
 
 [[package]]
@@ -3648,7 +3594,9 @@ name = "rustc_driver_impl"
 version = "0.0.0"
 dependencies = [
  "ctrlc",
+ "jiff",
  "libc",
+ "rustc_abi",
  "rustc_ast",
  "rustc_ast_lowering",
  "rustc_ast_passes",
@@ -3693,9 +3641,8 @@ dependencies = [
  "rustc_ty_utils",
  "serde_json",
  "shlex",
- "time",
  "tracing",
- "windows",
+ "windows 0.59.0",
 ]
 
 [[package]]
@@ -3730,10 +3677,12 @@ dependencies = [
  "rustc_abi",
  "rustc_ast",
  "rustc_ast_pretty",
+ "rustc_attr_data_structures",
  "rustc_data_structures",
  "rustc_error_codes",
  "rustc_error_messages",
  "rustc_fluent_macro",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_index",
  "rustc_lexer",
@@ -3748,7 +3697,7 @@ dependencies = [
  "termcolor",
  "termize",
  "tracing",
- "windows",
+ "windows 0.59.0",
 ]
 
 [[package]]
@@ -3763,6 +3712,7 @@ dependencies = [
  "rustc_errors",
  "rustc_feature",
  "rustc_fluent_macro",
+ "rustc_hir",
  "rustc_lexer",
  "rustc_lint_defs",
  "rustc_macros",
@@ -3794,7 +3744,7 @@ dependencies = [
  "fluent-syntax",
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "unic-langid",
 ]
 
@@ -3806,6 +3756,13 @@ version = "0.0.0"
 name = "rustc_graphviz"
 version = "0.0.0"
 
+[[package]]
+name = "rustc_hashes"
+version = "0.0.0"
+dependencies = [
+ "rustc-stable-hash",
+]
+
 [[package]]
 name = "rustc_hir"
 version = "0.0.0"
@@ -3814,7 +3771,9 @@ dependencies = [
  "rustc_abi",
  "rustc_arena",
  "rustc_ast",
+ "rustc_attr_data_structures",
  "rustc_data_structures",
+ "rustc_hashes",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
@@ -3848,7 +3807,6 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "rustc_type_ir",
  "smallvec",
  "tracing",
 ]
@@ -3860,6 +3818,7 @@ dependencies = [
  "rustc_abi",
  "rustc_ast",
  "rustc_ast_pretty",
+ "rustc_attr_data_structures",
  "rustc_hir",
  "rustc_span",
 ]
@@ -3871,7 +3830,6 @@ dependencies = [
  "itertools",
  "rustc_abi",
  "rustc_ast",
- "rustc_ast_ir",
  "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
@@ -3887,7 +3845,6 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_trait_selection",
- "rustc_type_ir",
  "smallvec",
  "tracing",
 ]
@@ -3896,13 +3853,14 @@ dependencies = [
 name = "rustc_incremental"
 version = "0.0.0"
 dependencies = [
- "rand",
+ "rand 0.9.0",
  "rustc_ast",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
  "rustc_fs_util",
  "rustc_graphviz",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_macros",
  "rustc_middle",
@@ -3929,14 +3887,13 @@ version = "0.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "rustc_infer"
 version = "0.0.0"
 dependencies = [
- "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
@@ -3955,8 +3912,8 @@ dependencies = [
 name = "rustc_interface"
 version = "0.0.0"
 dependencies = [
- "rustc-rayon",
  "rustc-rayon-core",
+ "rustc_abi",
  "rustc_ast",
  "rustc_ast_lowering",
  "rustc_ast_passes",
@@ -3990,7 +3947,6 @@ dependencies = [
  "rustc_query_impl",
  "rustc_query_system",
  "rustc_resolve",
- "rustc_serialize",
  "rustc_session",
  "rustc_span",
  "rustc_symbol_mangling",
@@ -4006,6 +3962,7 @@ name = "rustc_lexer"
 version = "0.0.0"
 dependencies = [
  "expect-test",
+ "memchr",
  "unicode-properties",
  "unicode-xid",
 ]
@@ -4032,7 +3989,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "rustc_type_ir",
+ "smallvec",
  "tracing",
  "unicode-security",
 ]
@@ -4064,7 +4021,6 @@ dependencies = [
 name = "rustc_log"
 version = "0.0.0"
 dependencies = [
- "rustc_span",
  "tracing",
  "tracing-core",
  "tracing-subscriber",
@@ -4077,7 +4033,7 @@ version = "0.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "synstructure",
 ]
 
@@ -4107,7 +4063,6 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
- "rustc_type_ir",
  "tempfile",
  "tracing",
 ]
@@ -4118,7 +4073,6 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "either",
- "field-offset",
  "gsgdt",
  "polonius-engine",
  "rustc-rayon-core",
@@ -4127,13 +4081,14 @@ dependencies = [
  "rustc_arena",
  "rustc_ast",
  "rustc_ast_ir",
- "rustc_attr_parsing",
+ "rustc_attr_data_structures",
  "rustc_data_structures",
  "rustc_error_messages",
  "rustc_errors",
  "rustc_feature",
  "rustc_fluent_macro",
  "rustc_graphviz",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_hir_pretty",
  "rustc_index",
@@ -4154,13 +4109,11 @@ dependencies = [
 name = "rustc_mir_build"
 version = "0.0.0"
 dependencies = [
- "either",
  "itertools",
  "rustc_abi",
  "rustc_apfloat",
  "rustc_arena",
  "rustc_ast",
- "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
@@ -4223,7 +4176,6 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "rustc_type_ir",
  "smallvec",
  "tracing",
 ]
@@ -4233,6 +4185,7 @@ name = "rustc_monomorphize"
 version = "0.0.0"
 dependencies = [
  "rustc_abi",
+ "rustc_ast",
  "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
@@ -4242,6 +4195,7 @@ dependencies = [
  "rustc_middle",
  "rustc_session",
  "rustc_span",
+ "rustc_symbol_mangling",
  "rustc_target",
  "serde",
  "serde_json",
@@ -4253,14 +4207,12 @@ name = "rustc_next_trait_solver"
 version = "0.0.0"
 dependencies = [
  "derive-where",
- "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
  "rustc_type_ir",
  "rustc_type_ir_macros",
- "smallvec",
  "tracing",
 ]
 
@@ -4269,6 +4221,7 @@ name = "rustc_parse"
 version = "0.0.0"
 dependencies = [
  "bitflags",
+ "rustc-literal-escaper",
  "rustc_ast",
  "rustc_ast_pretty",
  "rustc_data_structures",
@@ -4291,6 +4244,7 @@ dependencies = [
 name = "rustc_parse_format"
 version = "0.0.0"
 dependencies = [
+ "rustc-literal-escaper",
  "rustc_index",
  "rustc_lexer",
 ]
@@ -4301,6 +4255,7 @@ version = "0.0.0"
 dependencies = [
  "rustc_abi",
  "rustc_ast",
+ "rustc_ast_lowering",
  "rustc_ast_pretty",
  "rustc_attr_parsing",
  "rustc_data_structures",
@@ -4324,7 +4279,7 @@ dependencies = [
 name = "rustc_pattern_analysis"
 version = "0.0.0"
 dependencies = [
- "rustc-hash 2.1.0",
+ "rustc-hash 2.1.1",
  "rustc_abi",
  "rustc_apfloat",
  "rustc_arena",
@@ -4365,10 +4320,9 @@ dependencies = [
 name = "rustc_query_impl"
 version = "0.0.0"
 dependencies = [
- "field-offset",
  "measureme",
  "rustc_data_structures",
- "rustc_errors",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_index",
  "rustc_middle",
@@ -4376,7 +4330,6 @@ dependencies = [
  "rustc_serialize",
  "rustc_session",
  "rustc_span",
- "thin-vec",
  "tracing",
 ]
 
@@ -4384,22 +4337,24 @@ dependencies = [
 name = "rustc_query_system"
 version = "0.0.0"
 dependencies = [
+ "hashbrown",
  "parking_lot",
  "rustc-rayon-core",
+ "rustc_abi",
  "rustc_ast",
+ "rustc_attr_data_structures",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_feature",
  "rustc_fluent_macro",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
  "rustc_session",
  "rustc_span",
- "rustc_target",
  "smallvec",
- "thin-vec",
  "tracing",
 ]
 
@@ -4408,6 +4363,7 @@ name = "rustc_resolve"
 version = "0.0.0"
 dependencies = [
  "bitflags",
+ "itertools",
  "pulldown-cmark 0.11.3",
  "rustc_arena",
  "rustc_ast",
@@ -4419,7 +4375,6 @@ dependencies = [
  "rustc_feature",
  "rustc_fluent_macro",
  "rustc_hir",
- "rustc_index",
  "rustc_macros",
  "rustc_metadata",
  "rustc_middle",
@@ -4452,6 +4407,7 @@ name = "rustc_serialize"
 version = "0.0.0"
 dependencies = [
  "indexmap",
+ "rustc_hashes",
  "rustc_macros",
  "smallvec",
  "tempfile",
@@ -4465,6 +4421,7 @@ dependencies = [
  "bitflags",
  "getopts",
  "libc",
+ "rand 0.9.0",
  "rustc_abi",
  "rustc_ast",
  "rustc_data_structures",
@@ -4472,6 +4429,7 @@ dependencies = [
  "rustc_feature",
  "rustc_fluent_macro",
  "rustc_fs_util",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_lint_defs",
  "rustc_macros",
@@ -4481,7 +4439,7 @@ dependencies = [
  "smallvec",
  "termize",
  "tracing",
- "windows",
+ "windows 0.59.0",
 ]
 
 [[package]]
@@ -4489,7 +4447,6 @@ name = "rustc_smir"
 version = "0.0.0"
 dependencies = [
  "rustc_abi",
- "rustc_ast",
  "rustc_data_structures",
  "rustc_hir",
  "rustc_hir_pretty",
@@ -4498,7 +4455,7 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "scoped-tls",
- "stable_mir",
+ "serde",
  "tracing",
 ]
 
@@ -4513,6 +4470,7 @@ dependencies = [
  "md-5",
  "rustc_arena",
  "rustc_data_structures",
+ "rustc_hashes",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
@@ -4532,6 +4490,7 @@ dependencies = [
  "rustc_abi",
  "rustc_data_structures",
  "rustc_errors",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_middle",
  "rustc_session",
@@ -4557,9 +4516,13 @@ dependencies = [
 
 [[package]]
 name = "rustc_tools_util"
-version = "0.4.0"
+version = "0.4.2"
+
+[[package]]
+name = "rustc_tools_util"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d"
+checksum = "a3b75158011a63889ba12084cf1224baad7bcad50f6ee7c842f772b74aa148ed"
 
 [[package]]
 name = "rustc_trait_selection"
@@ -4568,7 +4531,6 @@ dependencies = [
  "itertools",
  "rustc_abi",
  "rustc_ast",
- "rustc_ast_ir",
  "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
@@ -4607,11 +4569,8 @@ version = "0.0.0"
 dependencies = [
  "itertools",
  "rustc_abi",
- "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_hir",
- "rustc_infer",
- "rustc_macros",
  "rustc_middle",
  "rustc_span",
  "tracing",
@@ -4623,10 +4582,10 @@ version = "0.0.0"
 dependencies = [
  "itertools",
  "rustc_abi",
- "rustc_ast_ir",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
+ "rustc_hashes",
  "rustc_hir",
  "rustc_index",
  "rustc_infer",
@@ -4636,7 +4595,6 @@ dependencies = [
  "rustc_span",
  "rustc_target",
  "rustc_trait_selection",
- "rustc_type_ir",
  "tracing",
 ]
 
@@ -4646,6 +4604,7 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "derive-where",
+ "ena",
  "indexmap",
  "rustc-hash 1.1.0",
  "rustc_ast_ir",
@@ -4666,7 +4625,7 @@ version = "0.0.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "synstructure",
 ]
 
@@ -4684,14 +4643,15 @@ name = "rustdoc"
 version = "0.0.0"
 dependencies = [
  "arrayvec",
+ "askama",
  "base64",
  "expect-test",
  "indexmap",
  "itertools",
  "minifier",
  "pulldown-cmark 0.9.6",
+ "pulldown-cmark-escape",
  "regex",
- "rinja",
  "rustdoc-json-types",
  "serde",
  "serde_json",
@@ -4710,6 +4670,7 @@ name = "rustdoc-gui-test"
 version = "0.1.0"
 dependencies = [
  "build_helper",
+ "camino",
  "compiletest",
  "getopts",
  "walkdir",
@@ -4720,7 +4681,7 @@ name = "rustdoc-json-types"
 version = "0.1.0"
 dependencies = [
  "bincode",
- "rustc-hash 2.1.0",
+ "rustc-hash 2.1.1",
  "serde",
  "serde_json",
 ]
@@ -4755,7 +4716,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "serde",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -4789,9 +4750,9 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.43"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
+checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
 dependencies = [
  "bitflags",
  "errno",
@@ -4802,9 +4763,9 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.19"
+version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
 
 [[package]]
 name = "ruzstd"
@@ -4817,9 +4778,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.18"
+version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
 
 [[package]]
 name = "same-file"
@@ -4868,40 +4829,39 @@ checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
 
 [[package]]
 name = "semver"
-version = "1.0.24"
+version = "1.0.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.217"
+version = "1.0.219"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.217"
+version = "1.0.219"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.135"
+version = "1.0.140"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
 dependencies = [
- "indexmap",
  "itoa",
  "memchr",
  "ryu",
@@ -4962,15 +4922,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
 [[package]]
 name = "similar"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
-
-[[package]]
-name = "siphasher"
-version = "0.3.11"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
 
 [[package]]
 name = "siphasher"
@@ -4989,15 +4943,15 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.13.2"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
 
 [[package]]
 name = "socket2"
-version = "0.5.8"
+version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
 dependencies = [
  "libc",
  "windows-sys 0.52.0",
@@ -5061,15 +5015,14 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 name = "stable_mir"
 version = "0.1.0-preview"
 dependencies = [
- "scoped-tls",
- "serde",
+ "rustc_smir",
 ]
 
 [[package]]
 name = "stacker"
-version = "0.1.17"
+version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b"
+checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9"
 dependencies = [
  "cc",
  "cfg-if",
@@ -5086,26 +5039,25 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 
 [[package]]
 name = "string_cache"
-version = "0.8.7"
+version = "0.8.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
+checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
 dependencies = [
  "new_debug_unreachable",
- "once_cell",
  "parking_lot",
- "phf_shared 0.10.0",
+ "phf_shared",
  "precomputed-hash",
  "serde",
 ]
 
 [[package]]
 name = "string_cache_codegen"
-version = "0.5.2"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
+checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
 dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
+ "phf_generator",
+ "phf_shared",
  "proc-macro2",
  "quote",
 ]
@@ -5156,9 +5108,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.96"
+version = "2.0.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -5173,7 +5125,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -5184,7 +5136,7 @@ checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be"
 dependencies = [
  "core-foundation-sys",
  "libc",
- "windows",
+ "windows 0.57.0",
 ]
 
 [[package]]
@@ -5199,9 +5151,9 @@ dependencies = [
 
 [[package]]
 name = "tar"
-version = "0.4.43"
+version = "0.4.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6"
+checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
 dependencies = [
  "filetime",
  "libc",
@@ -5210,13 +5162,12 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.15.0"
+version = "3.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
+checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
 dependencies = [
- "cfg-if",
  "fastrand",
- "getrandom",
+ "getrandom 0.3.2",
  "once_cell",
  "rustix",
  "windows-sys 0.59.0",
@@ -5253,16 +5204,6 @@ dependencies = [
  "winapi-util",
 ]
 
-[[package]]
-name = "terminal_size"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
-dependencies = [
- "rustix",
- "windows-sys 0.59.0",
-]
-
 [[package]]
 name = "termize"
 version = "0.1.1"
@@ -5279,16 +5220,16 @@ version = "0.1.0"
 dependencies = [
  "indicatif",
  "num",
- "rand",
- "rand_chacha",
+ "rand 0.9.0",
+ "rand_chacha 0.9.0",
  "rayon",
 ]
 
 [[package]]
 name = "thin-vec"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b"
+checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d"
 
 [[package]]
 name = "thiserror"
@@ -5301,11 +5242,11 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "2.0.11"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
 dependencies = [
- "thiserror-impl 2.0.11",
+ "thiserror-impl 2.0.12",
 ]
 
 [[package]]
@@ -5316,28 +5257,28 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.11"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "thorin-dwp"
-version = "0.8.0"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "813ba76597db32dc4f6992fd8bf8f394715b88d352fd97401da67dab6283b4c6"
+checksum = "9e9c1e705f82a260173f3eec93f2ff6d7807f23ad5a8cc2e7316a891733ea7a1"
 dependencies = [
- "gimli 0.30.0",
- "hashbrown 0.14.5",
+ "gimli 0.31.1",
+ "hashbrown",
  "object 0.36.7",
  "tracing",
 ]
@@ -5366,15 +5307,17 @@ name = "tidy"
 version = "0.1.0"
 dependencies = [
  "build_helper",
- "cargo_metadata 0.19.1",
+ "cargo_metadata 0.19.2",
  "fluent-syntax",
  "ignore",
  "miropt-test-tools",
  "regex",
- "rustc-hash 2.1.0",
+ "rustc-hash 2.1.1",
  "semver",
+ "serde",
  "similar",
  "termcolor",
+ "toml 0.7.8",
  "walkdir",
 ]
 
@@ -5392,37 +5335,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "time"
-version = "0.3.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
-dependencies = [
- "deranged",
- "itoa",
- "num-conv",
- "powerfmt",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
-
-[[package]]
-name = "time-macros"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
 [[package]]
 name = "tinystr"
 version = "0.7.6"
@@ -5435,9 +5347,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.8.1"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
+checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -5450,9 +5362,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.43.0"
+version = "1.44.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
+checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
 dependencies = [
  "backtrace",
  "bytes",
@@ -5499,15 +5411,9 @@ dependencies = [
  "serde",
  "serde_spanned",
  "toml_datetime",
- "winnow",
+ "winnow 0.5.40",
 ]
 
-[[package]]
-name = "topological-sort"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
-
 [[package]]
 name = "tracing"
 version = "0.1.37"
@@ -5528,7 +5434,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -5600,7 +5506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
 dependencies = [
  "cfg-if",
- "rand",
+ "rand 0.8.5",
  "static_assertions",
 ]
 
@@ -5615,9 +5521,9 @@ dependencies = [
 
 [[package]]
 name = "typenum"
-version = "1.17.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
 
 [[package]]
 name = "ucd-parse"
@@ -5636,9 +5542,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
 
 [[package]]
 name = "ui_test"
-version = "0.26.5"
+version = "0.29.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32ee4c40e5a5f9fa6864ff976473e5d6a6e9884b6ce68b40690d9f87e1994c83"
+checksum = "1211b1111c752c73b33073d2958072be08825fd97c9ab4d83444da361a06634b"
 dependencies = [
  "annotate-snippets 0.11.5",
  "anyhow",
@@ -5699,7 +5605,7 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b"
 dependencies = [
  "proc-macro-hack",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "unic-langid-impl",
 ]
 
@@ -5711,9 +5617,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.14"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
 
 [[package]]
 name = "unicode-normalization"
@@ -5837,11 +5743,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "uuid"
-version = "1.12.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
+checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
 dependencies = [
- "getrandom",
+ "getrandom 0.3.2",
 ]
 
 [[package]]
@@ -5878,6 +5784,15 @@ version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
 [[package]]
 name = "wasi-preview1-component-adapter-provider"
 version = "29.0.1"
@@ -5906,7 +5821,7 @@ dependencies = [
  "log",
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "wasm-bindgen-shared",
 ]
 
@@ -5928,7 +5843,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -5954,7 +5869,7 @@ dependencies = [
  "libc",
  "tempfile",
  "wasi-preview1-component-adapter-provider",
- "wasmparser 0.223.0",
+ "wasmparser 0.223.1",
  "wat",
  "windows-sys 0.59.0",
  "winsplit",
@@ -5971,29 +5886,39 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.219.1"
+version = "0.219.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54"
+checksum = "8aa79bcd666a043b58f5fa62b221b0b914dd901e6f620e8ab7371057a797f3e1"
 dependencies = [
  "leb128",
- "wasmparser 0.219.1",
+ "wasmparser 0.219.2",
 ]
 
 [[package]]
 name = "wasm-encoder"
-version = "0.223.0"
+version = "0.223.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464"
+checksum = "7a0a96fdeaee8fbeb4bd917fb8157d48c0d61c3b1f4ee4c363c8e8d68b2f4fe8"
 dependencies = [
  "leb128",
- "wasmparser 0.223.0",
+ "wasmparser 0.223.1",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.228.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d30290541f2d4242a162bbda76b8f2d8b1ac59eab3568ed6f2327d52c9b2c4"
+dependencies = [
+ "leb128fmt",
+ "wasmparser 0.228.0",
 ]
 
 [[package]]
 name = "wasm-metadata"
-version = "0.223.0"
+version = "0.223.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c730c3379d3d20e5a0245b0724b924483e853588ca8fba547c1e21f19e7d735"
+checksum = "e2e7e37883181704d96b89dbd8f1291be13694c71cd0663aebb94b46d235a377"
 dependencies = [
  "anyhow",
  "indexmap",
@@ -6002,15 +5927,15 @@ dependencies = [
  "serde_json",
  "spdx",
  "url",
- "wasm-encoder 0.223.0",
- "wasmparser 0.223.0",
+ "wasm-encoder 0.223.1",
+ "wasmparser 0.223.1",
 ]
 
 [[package]]
 name = "wasmparser"
-version = "0.219.1"
+version = "0.219.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5"
+checksum = "5220ee4c6ffcc0cb9d7c47398052203bc902c8ef3985b0c8134118440c0b2921"
 dependencies = [
  "bitflags",
  "indexmap",
@@ -6018,44 +5943,55 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.222.0"
+version = "0.222.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4adf50fde1b1a49c1add6a80d47aea500c88db70551805853aa8b88f3ea27ab5"
+checksum = "fa210fd1788e6b37a1d1930f3389c48e1d6ebd1a013d34fa4b7f9e3e3bf03146"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "wasmparser"
-version = "0.223.0"
+version = "0.223.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5a99faceb1a5a84dd6084ec4bfa4b2ab153b5793b43fd8f58b89232634afc35"
+checksum = "664b980991ed9a8c834eb528a8979ab1109edcf52dc05dd5751e2cc3fb31035d"
 dependencies = [
  "bitflags",
- "hashbrown 0.15.2",
+ "hashbrown",
  "indexmap",
  "semver",
  "serde",
 ]
 
+[[package]]
+name = "wasmparser"
+version = "0.228.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3"
+dependencies = [
+ "bitflags",
+ "indexmap",
+ "semver",
+]
+
 [[package]]
 name = "wast"
-version = "223.0.0"
+version = "228.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59b2ba8a2ff9f06194b7be9524f92e45e70149f4dacc0d0c7ad92b59ac875e4"
+checksum = "9e5aae124478cb51439f6587f074a3a5e835afd22751c59a87b2e2a882727c97"
 dependencies = [
  "bumpalo",
- "leb128",
+ "leb128fmt",
  "memchr",
  "unicode-width 0.2.0",
- "wasm-encoder 0.223.0",
+ "wasm-encoder 0.228.0",
 ]
 
 [[package]]
 name = "wat"
-version = "1.223.0"
+version = "1.228.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "662786915c427e4918ff01eabb3c4756d4d947cd8f635761526b4cc9da2eaaad"
+checksum = "7ec29c89a8d055df988de7236483bf569988ac3d6905899f6af5ef920f9385ad"
 dependencies = [
  "wast",
 ]
@@ -6111,39 +6047,63 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1"
+dependencies = [
+ "windows-core 0.59.0",
+ "windows-targets 0.53.0",
+]
+
 [[package]]
 name = "windows-bindgen"
-version = "0.58.0"
+version = "0.61.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91cd28d93c692351f3a6e5615567c56756e330bee1c99c6bdd57bfc5ab15f589"
+checksum = "ac1c59c20569610dd9ed784d5f003fb493ec57b4cf39d974eb03a84bb7156c90"
 dependencies = [
- "proc-macro2",
  "rayon",
  "serde",
  "serde_json",
- "syn 2.0.96",
- "windows-metadata",
 ]
 
 [[package]]
 name = "windows-core"
-version = "0.52.0"
+version = "0.57.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
 dependencies = [
+ "windows-implement 0.57.0",
+ "windows-interface 0.57.0",
+ "windows-result 0.1.2",
  "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "windows-core"
-version = "0.57.0"
+version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
+checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce"
 dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-result",
- "windows-targets 0.52.6",
+ "windows-implement 0.59.0",
+ "windows-interface 0.59.1",
+ "windows-result 0.3.2",
+ "windows-strings 0.3.1",
+ "windows-targets 0.53.0",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.61.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
+dependencies = [
+ "windows-implement 0.60.0",
+ "windows-interface 0.59.1",
+ "windows-link",
+ "windows-result 0.3.2",
+ "windows-strings 0.4.0",
 ]
 
 [[package]]
@@ -6154,7 +6114,29 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
 ]
 
 [[package]]
@@ -6165,14 +6147,25 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
 ]
 
 [[package]]
-name = "windows-metadata"
-version = "0.58.0"
+name = "windows-link"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e837f3c3012cfe9e7086302a93f441a7999439be1ad4c530d55d2f6d2921809"
+checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
 
 [[package]]
 name = "windows-result"
@@ -6183,6 +6176,33 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "windows-result"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+dependencies = [
+ "windows-link",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
@@ -6234,13 +6254,29 @@ dependencies = [
  "windows_aarch64_gnullvm 0.52.6",
  "windows_aarch64_msvc 0.52.6",
  "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm",
+ "windows_i686_gnullvm 0.52.6",
  "windows_i686_msvc 0.52.6",
  "windows_x86_64_gnu 0.52.6",
  "windows_x86_64_gnullvm 0.52.6",
  "windows_x86_64_msvc 0.52.6",
 ]
 
+[[package]]
+name = "windows-targets"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.53.0",
+ "windows_aarch64_msvc 0.53.0",
+ "windows_i686_gnu 0.53.0",
+ "windows_i686_gnullvm 0.53.0",
+ "windows_i686_msvc 0.53.0",
+ "windows_x86_64_gnu 0.53.0",
+ "windows_x86_64_gnullvm 0.53.0",
+ "windows_x86_64_msvc 0.53.0",
+]
+
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.5"
@@ -6253,6 +6289,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
@@ -6265,6 +6307,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
@@ -6277,12 +6325,24 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
 [[package]]
 name = "windows_i686_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
@@ -6295,6 +6355,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
@@ -6307,6 +6373,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
@@ -6319,6 +6391,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
@@ -6331,6 +6409,12 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
 [[package]]
 name = "winnow"
 version = "0.5.40"
@@ -6340,17 +6424,35 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "winnow"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "winsplit"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956"
 
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "wit-component"
-version = "0.223.0"
+version = "0.223.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c10ed2aeee4c8ec5715875f62f4a3de3608d6987165c116810d8c2908aa9d93b"
+checksum = "3fc2fcc52a79f6f010a89c867e53e06d4227f86c58984add3e37f32b8e7af5f0"
 dependencies = [
  "anyhow",
  "bitflags",
@@ -6359,17 +6461,17 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.223.0",
+ "wasm-encoder 0.223.1",
  "wasm-metadata",
- "wasmparser 0.223.0",
+ "wasmparser 0.223.1",
  "wit-parser",
 ]
 
 [[package]]
 name = "wit-parser"
-version = "0.223.0"
+version = "0.223.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92772f4dcacb804b275981eea1d920b12b377993b53307f1e33d87404e080281"
+checksum = "263fde17f1fbe55a413f16eb59094bf751795c6da469a05c3d45ea6c77df6b40"
 dependencies = [
  "anyhow",
  "id-arena",
@@ -6380,7 +6482,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.223.0",
+ "wasmparser 0.223.1",
 ]
 
 [[package]]
@@ -6395,14 +6497,17 @@ version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
 
+[[package]]
+name = "x"
+version = "0.1.1"
+
 [[package]]
 name = "xattr"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
+checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e"
 dependencies = [
  "libc",
- "linux-raw-sys",
  "rustix",
 ]
 
@@ -6444,49 +6549,48 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "synstructure",
 ]
 
 [[package]]
 name = "zerocopy"
-version = "0.7.35"
+version = "0.8.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
 dependencies = [
- "byteorder",
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.35"
+version = "0.8.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
 
 [[package]]
 name = "zerofrom"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
 dependencies = [
  "zerofrom-derive",
 ]
 
 [[package]]
 name = "zerofrom-derive"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
  "synstructure",
 ]
 
@@ -6509,5 +6613,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.96",
+ "syn 2.0.100",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index b773030b4cab..16ff0f61593a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,52 +1,53 @@
 [workspace]
 resolver = "2"
 members = [
+# tidy-alphabetical-start
   "compiler/rustc",
   "src/build_helper",
   "src/etc/test-float-parse",
-  "src/rustc-std-workspace/rustc-std-workspace-core",
   "src/rustc-std-workspace/rustc-std-workspace-alloc",
+  "src/rustc-std-workspace/rustc-std-workspace-core",
   "src/rustc-std-workspace/rustc-std-workspace-std",
   "src/rustdoc-json-types",
+  "src/tools/build-manifest",
+  "src/tools/bump-stage0",
   "src/tools/cargotest",
   "src/tools/clippy",
   "src/tools/clippy/clippy_dev",
+  "src/tools/collect-license-metadata",
   "src/tools/compiletest",
-  "src/tools/run-make-support",
-  "src/tools/error_index_generator",
+  "src/tools/coverage-dump",
+  "src/tools/features-status-dump",
+  "src/tools/generate-copyright",
+  "src/tools/generate-windows-sys",
+  "src/tools/html-checker",
+  "src/tools/jsondocck",
+  "src/tools/jsondoclint",
   "src/tools/linkchecker",
   "src/tools/lint-docs",
+  "src/tools/lld-wrapper",
+  "src/tools/llvm-bitcode-linker",
+  "src/tools/miri",
+  "src/tools/miri/cargo-miri",
   "src/tools/miropt-test-tools",
-  "src/tools/unstable-book-gen",
-  "src/tools/tidy",
-  "src/tools/tier-check",
-  "src/tools/build-manifest",
+  "src/tools/opt-dist",
   "src/tools/remote-test-client",
   "src/tools/remote-test-server",
+  "src/tools/replace-version-placeholder",
+  "src/tools/run-make-support",
   "src/tools/rust-installer",
   "src/tools/rustdoc",
-  "src/tools/rls",
-  "src/tools/rustfmt",
-  "src/tools/miri",
-  "src/tools/miri/cargo-miri",
+  "src/tools/rustdoc-gui-test",
   "src/tools/rustdoc-themes",
-  "src/tools/unicode-table-generator",
-  "src/tools/jsondocck",
-  "src/tools/jsondoclint",
-  "src/tools/llvm-bitcode-linker",
-  "src/tools/html-checker",
-  "src/tools/bump-stage0",
-  "src/tools/replace-version-placeholder",
-  "src/tools/lld-wrapper",
-  "src/tools/collect-license-metadata",
-  "src/tools/generate-copyright",
+  "src/tools/rustfmt",
   "src/tools/suggest-tests",
-  "src/tools/generate-windows-sys",
-  "src/tools/rustdoc-gui-test",
-  "src/tools/opt-dist",
-  "src/tools/coverage-dump",
-  "src/tools/rustc-perf-wrapper",
+  "src/tools/tidy",
+  "src/tools/tier-check",
+  "src/tools/unicode-table-generator",
+  "src/tools/unstable-book-gen",
   "src/tools/wasm-component-ld",
+  "src/tools/x",
+# tidy-alphabetical-end
 ]
 
 exclude = [
@@ -57,11 +58,6 @@ exclude = [
   "tests/rustdoc-gui",
   # HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`.
   "obj",
-  # The `x` binary is a thin wrapper that calls `x.py`, which initializes
-  # submodules, before which workspace members cannot be invoked because
-  # not all `Cargo.toml` files are available, so we exclude the `x` binary,
-  # so it can be invoked before the current checkout is set up.
-  "src/tools/x",
 ]
 
 [profile.release.package.rustc-rayon-core]
diff --git a/INSTALL.md b/INSTALL.md
index 74fcc58348b4..30e08201d6df 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -6,9 +6,9 @@ If you just want to install Rust, check out the [README.md](README.md) instead.*
 
 The Rust build system uses a Python script called `x.py` to build the compiler,
 which manages the bootstrapping process. It lives at the root of the project.
-It also uses a file named `config.toml` to determine various configuration
+It also uses a file named `bootstrap.toml` to determine various configuration
 settings for the build. You can see a full list of options in
-`config.example.toml`.
+`bootstrap.example.toml`.
 
 The `x.py` command can be run directly on most Unix systems in the following
 format:
@@ -115,7 +115,7 @@ See [the rustc-dev-guide for more info][sysllvm].
 
 This project provides a configure script and makefile (the latter of which just
 invokes `x.py`). `./configure` is the recommended way to programmatically
-generate a `config.toml`. `make` is not recommended (we suggest using `x.py`
+generate a `bootstrap.toml`. `make` is not recommended (we suggest using `x.py`
 directly), but it is supported and we try not to break it unnecessarily.
 
 ```sh
@@ -123,7 +123,7 @@ directly), but it is supported and we try not to break it unnecessarily.
 make && sudo make install
 ```
 
-`configure` generates a `config.toml` which can also be used with normal `x.py`
+`configure` generates a `bootstrap.toml` which can also be used with normal `x.py`
 invocations.
 
 ## Building on Windows
@@ -210,9 +210,13 @@ itself back on after some time).
 
 ### MSVC
 
-MSVC builds of Rust additionally require an installation of Visual Studio 2017
-(or later) so `rustc` can use its linker. The simplest way is to get
-[Visual Studio], check the "C++ build tools" and "Windows 10 SDK" workload.
+MSVC builds of Rust additionally requires an installation of:
+
+- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older
+  Visual Studio versions such as 2019 *may* work but aren't actively tested.
+- A recent Windows 10 or 11 SDK.
+
+The simplest way is to get [Visual Studio], check the "C++ build tools".
 
 [Visual Studio]: https://visualstudio.microsoft.com/downloads/
 
@@ -251,7 +255,7 @@ Windows build triples are:
     - `x86_64-pc-windows-msvc`
 
 The build triple can be specified by either specifying `--build=<triple>` when
-invoking `x.py` commands, or by creating a `config.toml` file (as described in
+invoking `x.py` commands, or by creating a `bootstrap.toml` file (as described in
 [Building on a Unix-like system](#building-on-a-unix-like-system)), and passing
 `--set build.build=<triple>` to `./configure`.
 
diff --git a/README.md b/README.md
index d84d96a0e917..611260470f12 100644
--- a/README.md
+++ b/README.md
@@ -67,11 +67,11 @@ See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT), and
 trademarks and logos (the "Rust Trademarks").
 
 If you want to use these names or brands, please read the
-[media guide][media-guide].
+[Rust language trademark policy][trademark-policy].
 
 Third-party logos may be subject to third-party copyrights and trademarks. See
 [Licenses][policies-licenses] for details.
 
-[rust-foundation]: https://foundation.rust-lang.org/
-[media-guide]: https://foundation.rust-lang.org/policies/logo-policy-and-media-guide/
+[rust-foundation]: https://rustfoundation.org/
+[trademark-policy]: https://rustfoundation.org/policy/rust-trademark-policy/
 [policies-licenses]: https://www.rust-lang.org/policies/licenses
diff --git a/RELEASES.md b/RELEASES.md
index d8d284ca1fa8..0948eb85192e 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,8 +1,287 @@
+Version 1.86.0 (2025-04-03)
+==========================
+
+<a id="1.86.0-Language"></a>
+
+Language
+--------
+- [Stabilize upcasting trait objects to supertraits.](https://github.com/rust-lang/rust/pull/134367)
+- [Allow safe functions to be marked with the `#[target_feature]` attribute.](https://github.com/rust-lang/rust/pull/134090)
+- [The `missing_abi` lint now warns-by-default.](https://github.com/rust-lang/rust/pull/132397)
+- Rust now lints about double negations, to catch cases that might have intended to be a prefix decrement operator (`--x`) as written in other languages. This was previously a clippy lint, `clippy::double_neg`, and is [now available directly in Rust as `double_negations`.](https://github.com/rust-lang/rust/pull/126604)
+- [More pointers are now detected as definitely not-null based on their alignment in const eval.](https://github.com/rust-lang/rust/pull/133700)
+- [Empty `repr()` attribute applied to invalid items are now correctly rejected.](https://github.com/rust-lang/rust/pull/133925)
+- [Inner attributes `#![test]` and `#![rustfmt::skip]` are no longer accepted in more places than intended.](https://github.com/rust-lang/rust/pull/134276)
+
+<a id="1.86.0-Compiler"></a>
+
+Compiler
+--------
+- [Debug-assert that raw pointers are non-null on access.](https://github.com/rust-lang/rust/pull/134424)
+- [Change `-O` to mean `-C opt-level=3` instead of `-C opt-level=2` to match Cargo's defaults.](https://github.com/rust-lang/rust/pull/135439)
+- [Fix emission of `overflowing_literals` under certain macro environments.](https://github.com/rust-lang/rust/pull/136393)
+
+<a id="1.86.0-Platform-Support"></a>
+
+Platform Support
+----------------
+- [Replace `i686-unknown-redox` target with `i586-unknown-redox`.](https://github.com/rust-lang/rust/pull/136698)
+- [Increase baseline CPU of `i686-unknown-hurd-gnu` to Pentium 4.](https://github.com/rust-lang/rust/pull/136700)
+- New tier 3 targets:
+  - [`{aarch64-unknown,x86_64-pc}-nto-qnx710_iosock`](https://github.com/rust-lang/rust/pull/133631).
+    For supporting Neutrino QNX 7.1 with `io-socket` network stack.
+  - [`{aarch64-unknown,x86_64-pc}-nto-qnx800`](https://github.com/rust-lang/rust/pull/133631).
+    For supporting Neutrino QNX 8.0 (`no_std`-only).
+  - [`{x86_64,i686}-win7-windows-gnu`](https://github.com/rust-lang/rust/pull/134609).
+    Intended for backwards compatibility with Windows 7. `{x86_64,i686}-win7-windows-msvc` are the Windows MSVC counterparts that already exist as Tier 3 targets.
+  - [`amdgcn-amd-amdhsa`](https://github.com/rust-lang/rust/pull/134740).
+  - [`x86_64-pc-cygwin`](https://github.com/rust-lang/rust/pull/134999).
+  - [`{mips,mipsel}-mti-none-elf`](https://github.com/rust-lang/rust/pull/135074).
+    Initial bare-metal support.
+  - [`m68k-unknown-none-elf`](https://github.com/rust-lang/rust/pull/135085).
+  - [`armv7a-nuttx-{eabi,eabihf}`, `aarch64-unknown-nuttx`, and `thumbv7a-nuttx-{eabi,eabihf}`](https://github.com/rust-lang/rust/pull/135757).
+
+Refer to Rust's [platform support page][platform-support-doc]
+for more information on Rust's tiered platform support.
+
+<a id="1.86.0-Libraries"></a>
+
+Libraries
+---------
+- The type of `FromBytesWithNulError` in `CStr::from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError>` was [changed from an opaque struct to an enum](https://github.com/rust-lang/rust/pull/134143), allowing users to examine why the conversion failed.
+- [Remove `RustcDecodable` and `RustcEncodable`.](https://github.com/rust-lang/rust/pull/134272)
+- [Deprecate libtest's `--logfile` option.](https://github.com/rust-lang/rust/pull/134283)
+- [On recent versions of Windows, `std::fs::remove_file` will now remove read-only files.](https://github.com/rust-lang/rust/pull/134679)
+
+<a id="1.86.0-Stabilized-APIs"></a>
+
+Stabilized APIs
+---------------
+
+- [`{float}::next_down`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.next_down)
+- [`{float}::next_up`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.next_up)
+- [`<[_]>::get_disjoint_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_disjoint_mut)
+- [`<[_]>::get_disjoint_unchecked_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_disjoint_unchecked_mut)
+- [`slice::GetDisjointMutError`](https://doc.rust-lang.org/stable/std/slice/enum.GetDisjointMutError.html)
+- [`HashMap::get_disjoint_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.get_disjoint_mut)
+- [`HashMap::get_disjoint_unchecked_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.get_disjoint_unchecked_mut)
+- [`NonZero::count_ones`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.count_ones)
+- [`Vec::pop_if`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop_if)
+- [`sync::Once::wait`](https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.wait)
+- [`sync::Once::wait_force`](https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.wait_force)
+- [`sync::OnceLock::wait`](https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html#method.wait)
+
+These APIs are now stable in const contexts:
+
+- [`hint::black_box`](https://doc.rust-lang.org/stable/std/hint/fn.black_box.html)
+- [`io::Cursor::get_mut`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.get_mut)
+- [`io::Cursor::set_position`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.set_position)
+- [`str::is_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.is_char_boundary)
+- [`str::split_at`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at)
+- [`str::split_at_checked`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_checked)
+- [`str::split_at_mut`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_mut)
+- [`str::split_at_mut_checked`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_mut_checked)
+
+<a id="1.86.0-Cargo"></a>
+
+Cargo
+-----
+- [When merging, replace rather than combine configuration keys that refer to a program path and its arguments.](https://github.com/rust-lang/cargo/pull/15066/)
+- [Error if both `--package` and `--workspace` are passed but the requested package is missing.](https://github.com/rust-lang/cargo/pull/15071/) This was previously silently ignored, which was considered a bug since missing packages should be reported.
+- [Deprecate the token argument in `cargo login` to avoid shell history leaks.](https://github.com/rust-lang/cargo/pull/15057/)
+- [Simplify the implementation of `SourceID` comparisons.](https://github.com/rust-lang/cargo/pull/14980/) This may potentially change behavior if the canonicalized URL compares differently in alternative registries.
+
+<a id="1.86.0-Rustdoc"></a>
+
+Rustdoc
+-----
+- [Add a sans-serif font setting.](https://github.com/rust-lang/rust/pull/133636)
+
+<a id="1.86.0-Compatibility-Notes"></a>
+
+Compatibility Notes
+-------------------
+- [The `wasm_c_abi` future compatibility warning is now a hard error.](https://github.com/rust-lang/rust/pull/133951)
+  Users of `wasm-bindgen` should upgrade to at least version 0.2.89, otherwise compilation will fail.
+- [Remove long-deprecated no-op attributes `#![no_start]` and `#![crate_id]`.](https://github.com/rust-lang/rust/pull/134300)
+- [The future incompatibility lint `cenum_impl_drop_cast` has been made into a hard error.](https://github.com/rust-lang/rust/pull/135964) This means it is now an error to cast a field-less enum to an integer if the enum implements `Drop`.
+- [SSE2 is now required for "i686" 32-bit x86 hard-float targets; disabling it causes a warning that will become a hard error eventually.](https://github.com/rust-lang/rust/pull/137037)
+  To compile for pre-SSE2 32-bit x86, use a "i586" target instead.
+
+<a id="1.86.0-Internal-Changes"></a>
+
+Internal Changes
+----------------
+
+These changes do not affect any public interfaces of Rust, but they represent
+significant improvements to the performance or internals of rustc and related
+tools.
+
+- [Build the rustc on AArch64 Linux with ThinLTO + PGO.](https://github.com/rust-lang/rust/pull/133807)
+  The ARM 64-bit compiler (AArch64) on Linux is now optimized with ThinLTO and PGO, similar to the optimizations we have already performed for the x86-64 compiler on Linux. This should make it up to 30% faster.
+
+
+Version 1.85.1 (2025-03-18)
+==========================
+
+<a id="1.85.1"></a>
+
+- [Fix the doctest-merging feature of the 2024 Edition.](https://github.com/rust-lang/rust/pull/137899/)
+- [Relax some `target_feature` checks when generating docs.](https://github.com/rust-lang/rust/pull/137632/)
+- [Fix errors in `std::fs::rename` on Windows 10, version 1607.](https://github.com/rust-lang/rust/pull/137528/)
+- [Downgrade bootstrap `cc` to fix custom targets.](https://github.com/rust-lang/rust/pull/137460/)
+- [Skip submodule updates when building Rust from a source tarball.](https://github.com/rust-lang/rust/pull/137338/)
+
+Version 1.85.0 (2025-02-20)
+==========================
+
+<a id="1.85.0-Language"></a>
+
+Language
+--------
+- [The 2024 Edition is now stable.](https://github.com/rust-lang/rust/pull/133349)
+  See [the edition guide](https://doc.rust-lang.org/nightly/edition-guide/rust-2024/index.html) for more details.
+- [Stabilize async closures](https://github.com/rust-lang/rust/pull/132706)
+  See [RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html) for more details.
+- [Stabilize `#[diagnostic::do_not_recommend]`](https://github.com/rust-lang/rust/pull/132056)
+- [Add `unpredictable_function_pointer_comparisons` lint to warn against function pointer comparisons](https://github.com/rust-lang/rust/pull/118833)
+- [Lint on combining `#[no_mangle]` and `#[export_name]` attributes.](https://github.com/rust-lang/rust/pull/131558)
+
+<a id="1.85.0-Compiler"></a>
+
+Compiler
+--------
+- [The unstable flag `-Zpolymorphize` has been removed](https://github.com/rust-lang/rust/pull/133883), see https://github.com/rust-lang/compiler-team/issues/810 for some background.
+
+<a id="1.85.0-Platform-Support"></a>
+
+Platform Support
+----------------
+- [Promote `powerpc64le-unknown-linux-musl` to tier 2 with host tools](https://github.com/rust-lang/rust/pull/133801)
+
+Refer to Rust's [platform support page][platform-support-doc]
+for more information on Rust's tiered platform support.
+
+<a id="1.85.0-Libraries"></a>
+
+Libraries
+---------
+- [Panics in the standard library now have a leading `library/` in their path](https://github.com/rust-lang/rust/pull/132390)
+- [`std::env::home_dir()` on Windows now ignores the non-standard `$HOME` environment variable](https://github.com/rust-lang/rust/pull/132515)
+
+  It will be un-deprecated in a subsequent release.
+- [Add `AsyncFn*` to the prelude in all editions.](https://github.com/rust-lang/rust/pull/132611)
+
+<a id="1.85.0-Stabilized-APIs"></a>
+
+Stabilized APIs
+---------------
+
+- [`BuildHasherDefault::new`](https://doc.rust-lang.org/stable/std/hash/struct.BuildHasherDefault.html#method.new)
+- [`ptr::fn_addr_eq`](https://doc.rust-lang.org/std/ptr/fn.fn_addr_eq.html)
+- [`io::ErrorKind::QuotaExceeded`](https://doc.rust-lang.org/stable/std/io/enum.ErrorKind.html#variant.QuotaExceeded)
+- [`io::ErrorKind::CrossesDevices`](https://doc.rust-lang.org/stable/std/io/enum.ErrorKind.html#variant.CrossesDevices)
+- [`{float}::midpoint`](https://doc.rust-lang.org/core/primitive.f32.html#method.midpoint)
+- [Unsigned `{integer}::midpoint`](https://doc.rust-lang.org/std/primitive.u64.html#method.midpoint)
+- [`NonZeroU*::midpoint`](https://doc.rust-lang.org/std/num/type.NonZeroU32.html#method.midpoint)
+- [impl `std::iter::Extend` for tuples with arity 1 through 12](https://doc.rust-lang.org/stable/std/iter/trait.Extend.html#impl-Extend%3C(A,)%3E-for-(EA,))
+- [`FromIterator<(A, ...)>` for tuples with arity 1 through 12](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html#impl-FromIterator%3C(EA,)%3E-for-(A,))
+- [`std::task::Waker::noop`](https://doc.rust-lang.org/stable/std/task/struct.Waker.html#method.noop)
+
+These APIs are now stable in const contexts:
+
+- [`mem::size_of_val`](https://doc.rust-lang.org/stable/std/mem/fn.size_of_val.html)
+- [`mem::align_of_val`](https://doc.rust-lang.org/stable/std/mem/fn.align_of_val.html)
+- [`Layout::for_value`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.for_value)
+- [`Layout::align_to`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.align_to)
+- [`Layout::pad_to_align`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.pad_to_align)
+- [`Layout::extend`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.extend)
+- [`Layout::array`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.array)
+- [`std::mem::swap`](https://doc.rust-lang.org/stable/std/mem/fn.swap.html)
+- [`std::ptr::swap`](https://doc.rust-lang.org/stable/std/ptr/fn.swap.html)
+- [`NonNull::new`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.new)
+- [`HashMap::with_hasher`](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html#method.with_hasher)
+- [`HashSet::with_hasher`](https://doc.rust-lang.org/stable/std/collections/struct.HashSet.html#method.with_hasher)
+- [`BuildHasherDefault::new`](https://doc.rust-lang.org/stable/std/hash/struct.BuildHasherDefault.html#method.new)
+- [`<float>::recip`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.recip)
+- [`<float>::to_degrees`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.to_degrees)
+- [`<float>::to_radians`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.to_radians)
+- [`<float>::max`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.max)
+- [`<float>::min`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.min)
+- [`<float>::clamp`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.clamp)
+- [`<float>::abs`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.abs)
+- [`<float>::signum`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.signum)
+- [`<float>::copysign`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.copysign)
+- [`MaybeUninit::write`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.write)
+
+<a id="1.85.0-Cargo"></a>
+
+Cargo
+-----
+- [Add future-incompatibility warning against keywords in cfgs and add raw-idents](https://github.com/rust-lang/cargo/pull/14671/)
+- [Stabilize higher precedence trailing flags](https://github.com/rust-lang/cargo/pull/14900/)
+- [Pass `CARGO_CFG_FEATURE` to build scripts](https://github.com/rust-lang/cargo/pull/14902/)
+
+<a id="1.85.0-Rustdoc"></a>
+
+Rustdoc
+-----
+- [Doc comment on impl blocks shows the first line, even when the impl block is collapsed](https://github.com/rust-lang/rust/pull/132155)
+
+<a id="1.85.0-Compatibility-Notes"></a>
+
+Compatibility Notes
+-------------------
+- [`rustc` no longer treats the `test` cfg as a well known check-cfg](https://github.com/rust-lang/rust/pull/131729), instead it is up to the build systems and users of `--check-cfg`[^check-cfg] to set it as a well known cfg using `--check-cfg=cfg(test)`.
+
+  This is done to enable build systems like Cargo to set it conditionally, as not all source files are suitable for unit tests.
+  [Cargo (for now) unconditionally sets the `test` cfg as a well known cfg](https://github.com/rust-lang/cargo/pull/14963).
+  [^check-cfg]: https://doc.rust-lang.org/nightly/rustc/check-cfg.html
+- [Disable potentially incorrect type inference if there are trivial and non-trivial where-clauses](https://github.com/rust-lang/rust/pull/132325)
+- `std::env::home_dir()` has been deprecated for years, because it can give surprising results in some Windows configurations if the `HOME` environment variable is set (which is not the normal configuration on Windows). We had previously avoided changing its behavior, out of concern for compatibility with code depending on this non-standard configuration. Given how long this function has been deprecated, we're now fixing its behavior as a bugfix. A subsequent release will remove the deprecation for this function.
+- [Make `core::ffi::c_char` signedness more closely match that of the platform-default `char`](https://github.com/rust-lang/rust/pull/132975)
+
+  This changed `c_char` from an `i8` to `u8` or vice versa on many Tier 2 and 3
+  targets (mostly Arm and RISC-V embedded targets). The new definition may
+  result in compilation failures but fixes compatibility issues with C.
+
+  The `libc` crate matches this change as of its 0.2.169 release.
+- [When compiling a nested `macro_rules` macro from an external crate, the content of the inner `macro_rules` is now built with the edition of the external crate, not the local crate.](https://github.com/rust-lang/rust/pull/133274)
+- [Increase `sparcv9-sun-solaris` and `x86_64-pc-solaris` Solaris baseline to 11.4.](https://github.com/rust-lang/rust/pull/133293)
+- [Show `abi_unsupported_vector_types` lint in future breakage reports](https://github.com/rust-lang/rust/pull/133374)
+- [Error if multiple super-trait instantiations of `dyn Trait` need associated types to be specified but only one is provided](https://github.com/rust-lang/rust/pull/133392)
+- [Change `powerpc64-ibm-aix` default `codemodel` to large](https://github.com/rust-lang/rust/pull/133811)
+
+<a id="1.85.0-Internal-Changes"></a>
+
+Internal Changes
+----------------
+
+These changes do not affect any public interfaces of Rust, but they represent
+significant improvements to the performance or internals of rustc and related
+tools.
+
+- [Build `x86_64-unknown-linux-gnu` with LTO for C/C++ code (e.g., `jemalloc`)](https://github.com/rust-lang/rust/pull/134690)
+
+Version 1.84.1 (2025-01-30)
+==========================
+
+<a id="1.84.1"></a>
+
+- [Fix ICE 132920 in duplicate-crate diagnostics.](https://github.com/rust-lang/rust/pull/133304/)
+- [Fix errors for overlapping impls in incremental rebuilds.](https://github.com/rust-lang/rust/pull/133828/)
+- [Fix slow compilation related to the next-generation trait solver.](https://github.com/rust-lang/rust/pull/135618/)
+- [Fix debuginfo when LLVM's location discriminator value limit is exceeded.](https://github.com/rust-lang/rust/pull/135643/)
+- Fixes for building Rust from source:
+  - [Only try to distribute `llvm-objcopy` if llvm tools are enabled.](https://github.com/rust-lang/rust/pull/134240/)
+  - [Add Profile Override for Non-Git Sources.](https://github.com/rust-lang/rust/pull/135433/)
+  - [Resolve symlinks of LLVM tool binaries before copying them.](https://github.com/rust-lang/rust/pull/135585/)
+  - [Make it possible to use ci-rustc on tarball sources.](https://github.com/rust-lang/rust/pull/135722/)
+
 Version 1.84.0 (2025-01-09)
 ==========================
 
-<a id="
-Language"></a>
+<a id="1.84.0-Language"></a>
 
 Language
 --------
@@ -573,6 +852,7 @@ Libraries
 - [Replace sort implementations with stable `driftsort` and unstable `ipnsort`.](https://github.com/rust-lang/rust/pull/124032/) All `slice::sort*` and `slice::select_nth*` methods are expected to see significant performance improvements. See the [research project](https://github.com/Voultapher/sort-research-rs) for more details.
 - [Document behavior of `create_dir_all` with respect to empty paths.](https://github.com/rust-lang/rust/pull/125112/)
 - [Fix interleaved output in the default panic hook when multiple threads panic simultaneously.](https://github.com/rust-lang/rust/pull/127397/)
+- Fix `Command`'s batch files argument escaping not working when file name has trailing whitespace or periods (CVE-2024-43402).
 
 <a id="1.81.0-Stabilized-APIs"></a>
 
@@ -2136,7 +2416,7 @@ Compatibility Notes
 - [Cargo denies `CARGO_HOME` in the `[env]` configuration table. Cargo itself doesn't pick up this value, but recursive calls to cargo would, which was not intended.](https://github.com/rust-lang/cargo/pull/11644/)
 - [Debuginfo for build dependencies is now off if not explicitly set. This is expected to improve the overall build time.](https://github.com/rust-lang/cargo/pull/11252/)
 - [The Rust distribution no longer always includes rustdoc](https://github.com/rust-lang/rust/pull/106886)
-  If `tools = [...]` is set in config.toml, we will respect a missing rustdoc in that list. By
+  If `tools = [...]` is set in bootstrap.toml, we will respect a missing rustdoc in that list. By
   default rustdoc remains included. To retain the prior behavior explicitly add `"rustdoc"` to the
   list.
 
@@ -5122,7 +5402,7 @@ related tools.
 
 - [Building `rustc` from source now uses `ninja` by default over `make`.][74922]
   You can continue building with `make` by setting `ninja=false` in
-  your `config.toml`.
+  your `bootstrap.toml`.
 - [cg_llvm: `fewer_names` in `uncached_llvm_type`][76030]
 - [Made `ensure_sufficient_stack()` non-generic][76680]
 
diff --git a/REUSE.toml b/REUSE.toml
index 6b16d97ed806..816c6d730c82 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -22,7 +22,7 @@ path = [
     "Cargo.lock",
     "Cargo.toml",
     "CODE_OF_CONDUCT.md",
-    "config.example.toml",
+    "bootstrap.example.toml",
     "configure",
     "CONTRIBUTING.md",
     "COPYRIGHT",
@@ -92,7 +92,7 @@ SPDX-FileCopyrightText = "2015 Anders Kaseorg <andersk@mit.edu>"
 SPDX-License-Identifier = "MIT"
 
 [[annotations]]
-path = "src/librustdoc/html/static/fonts/FiraSans**"
+path = "src/librustdoc/html/static/fonts/Fira**"
 precedence = "override"
 SPDX-FileCopyrightText = ["2014, Mozilla Foundation", "2014, Telefonica S.A."]
 SPDX-License-Identifier = "OFL-1.1"
diff --git a/bootstrap.example.toml b/bootstrap.example.toml
new file mode 100644
index 000000000000..0927f648635c
--- /dev/null
+++ b/bootstrap.example.toml
@@ -0,0 +1,1002 @@
+# Sample TOML configuration file for building Rust.
+#
+# To configure bootstrap, run `./configure` or `./x.py setup`.
+# See https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html#create-a-bootstraptoml for more information.
+#
+# All options are commented out by default in this file, and they're commented
+# out with their default values. The build system by default looks for
+# `bootstrap.toml` in the current directory of a build for build configuration, but
+# a custom configuration file can also be specified with `--config` to the build
+# system.
+
+# =============================================================================
+# Global Settings
+# =============================================================================
+
+# Use different pre-set defaults than the global defaults.
+#
+# See `src/bootstrap/defaults` for more information.
+# Note that this has no default value (x.py uses the defaults in `bootstrap.example.toml`).
+#profile = <none>
+
+# Keeps track of major changes made to this configuration.
+#
+# This value also represents ID of the PR that caused major changes. Meaning,
+# you can visit github.com/rust-lang/rust/pull/{change-id} to check for more details.
+#
+# A 'major change' includes any of the following
+#  - A new option
+#  - A change in the default values
+#
+# If the change-id does not match the version currently in use, x.py will
+# display the changes made to the bootstrap.
+# To suppress these warnings, you can set change-id = "ignore".
+#change-id = <latest change id in src/bootstrap/src/utils/change_tracker.rs>
+
+# =============================================================================
+# Tweaking how LLVM is compiled
+# =============================================================================
+[llvm]
+
+# Whether to use Rust CI built LLVM instead of locally building it.
+#
+# Unless you're developing for a target where Rust CI doesn't build a compiler
+# toolchain or changing LLVM locally, you probably want to leave this enabled.
+#
+# Set this to `true` to download if CI llvm available otherwise it builds
+# from `src/llvm-project`.
+#
+# Set this to `"if-unchanged"` to download only if the llvm-project has not
+# been modified. You can also use this if you are unsure whether you're on a
+# tier 1 target. All tier 1 targets are currently supported.
+
+# Currently, we only support this when building LLVM for the build triple.
+#
+# Note that many of the LLVM options are not currently supported for
+# downloading. Currently only the "assertions" option can be toggled.
+#download-ci-llvm = true
+
+# Indicates whether the LLVM build is a Release or Debug build
+#optimize = true
+
+# Indicates whether LLVM should be built with ThinLTO. Note that this will
+# only succeed if you use clang, lld, llvm-ar, and llvm-ranlib in your C/C++
+# toolchain (see the `cc`, `cxx`, `linker`, `ar`, and `ranlib` options below).
+# More info at: https://clang.llvm.org/docs/ThinLTO.html#clang-bootstrap
+#thin-lto = false
+
+# Indicates whether an LLVM Release build should include debug info
+#release-debuginfo = false
+
+# Indicates whether the LLVM assertions are enabled or not
+# NOTE: When assertions are disabled, bugs in the integration between rustc and LLVM can lead to
+# unsoundness (segfaults, etc.) in the rustc process itself, not just in the generated code.
+#assertions = false
+
+# Indicates whether the LLVM testsuite is enabled in the build or not. Does
+# not execute the tests as part of the build as part of x.py build et al,
+# just makes it possible to do `ninja check-llvm` in the staged LLVM build
+# directory when doing LLVM development as part of Rust development.
+#tests = false
+
+# Indicates whether the LLVM plugin is enabled or not
+#plugins = false
+
+# Whether to build Enzyme as AutoDiff backend.
+#enzyme = false
+
+# Whether to build LLVM with support for it's gpu offload runtime.
+#offload = false
+
+# When true, link libstdc++ statically into the rustc_llvm.
+# This is useful if you don't want to use the dynamic version of that
+# library provided by LLVM.
+#static-libstdcpp = false
+
+# Enable LLVM to use zstd for compression.
+#libzstd = false
+
+# Whether to use Ninja to build LLVM. This runs much faster than make.
+#ninja = true
+
+# LLVM targets to build support for.
+# Note: this is NOT related to Rust compilation targets. However, as Rust is
+# dependent on LLVM for code generation, turning targets off here WILL lead to
+# the resulting rustc being unable to compile for the disabled architectures.
+#
+# To add support for new targets, see https://rustc-dev-guide.rust-lang.org/building/new-target.html.
+#targets = "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86"
+
+# LLVM experimental targets to build support for. These targets are specified in
+# the same format as above, but since these targets are experimental, they are
+# not built by default and the experimental Rust compilation targets that depend
+# on them will not work unless the user opts in to building them.
+#experimental-targets = "AVR;M68k;CSKY"
+
+# Cap the number of parallel linker invocations when compiling LLVM.
+# This can be useful when building LLVM with debug info, which significantly
+# increases the size of binaries and consequently the memory required by
+# each linker process.
+# If set to 0, linker invocations are treated like any other job and
+# controlled by bootstrap's -j parameter.
+#link-jobs = 0
+
+# Whether to build LLVM as a dynamically linked library (as opposed to statically linked).
+# Under the hood, this passes `--shared` to llvm-config.
+# NOTE: To avoid performing LTO multiple times, we suggest setting this to `true` when `thin-lto` is enabled.
+#link-shared = llvm.thin-lto
+
+# When building llvm, this configures what is being appended to the version.
+# To use LLVM version as is, provide an empty string.
+#version-suffix = if rust.channel == "dev" { "-rust-dev" } else { "-rust-$version-$channel" }
+
+# On MSVC you can compile LLVM with clang-cl, but the test suite doesn't pass
+# with clang-cl, so this is special in that it only compiles LLVM with clang-cl.
+# Note that this takes a /path/to/clang-cl, not a boolean.
+#clang-cl = cc
+
+# Pass extra compiler and linker flags to the LLVM CMake build.
+#cflags = ""
+#cxxflags = ""
+#ldflags = ""
+
+# Use libc++ when building LLVM instead of libstdc++. This is the default on
+# platforms already use libc++ as the default C++ library, but this option
+# allows you to use libc++ even on platforms when it's not. You need to ensure
+# that your host compiler ships with libc++.
+#use-libcxx = false
+
+# The value specified here will be passed as `-DLLVM_USE_LINKER` to CMake.
+#use-linker = <none> (path)
+
+# Whether or not to specify `-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=YES`
+#allow-old-toolchain = false
+
+# Whether to include the Polly optimizer.
+#polly = false
+
+# Whether to build the clang compiler.
+#clang = false
+
+# Whether to enable llvm compilation warnings.
+#enable-warnings = false
+
+# Custom CMake defines to set when building LLVM.
+#build-config = {}
+
+# =============================================================================
+# Tweaking how GCC is compiled
+# =============================================================================
+[gcc]
+# Download GCC from CI instead of building it locally.
+# Note that this will attempt to download GCC even if there are local
+# modifications to the `src/gcc` submodule.
+# Currently, this is only supported for the `x86_64-unknown-linux-gnu` target.
+# download-ci-gcc = false
+
+# =============================================================================
+# General build configuration options
+# =============================================================================
+[build]
+
+# The default stage to use for the `check` subcommand
+#check-stage = 0
+
+# The default stage to use for the `doc` subcommand
+#doc-stage = 0
+
+# The default stage to use for the `build` subcommand
+#build-stage = 1
+
+# The default stage to use for the `test` subcommand
+#test-stage = 1
+
+# The default stage to use for the `dist` subcommand
+#dist-stage = 2
+
+# The default stage to use for the `install` subcommand
+#install-stage = 2
+
+# The default stage to use for the `bench` subcommand
+#bench-stage = 2
+
+# A descriptive string to be appended to version output (e.g., `rustc --version`),
+# which is also used in places like debuginfo `DW_AT_producer`. This may be useful for
+# supplementary build information, like distro-specific package versions.
+#
+# The Rust compiler will differentiate between versions of itself, including
+# based on this string, which means that if you wish to be compatible with
+# upstream Rust you need to set this to "". However, note that if you set this to "" but
+# are not actually compatible -- for example if you've backported patches that change
+# behavior -- this may lead to miscompilations or other bugs.
+#description = ""
+
+# Build triple for the pre-compiled snapshot compiler. If `rustc` is set, this must match its host
+# triple (see `rustc --version --verbose`; cross-compiling the rust build system itself is NOT
+# supported). If `rustc` is unset, this must be a platform with pre-compiled host tools
+# (https://doc.rust-lang.org/nightly/rustc/platform-support.html). The current platform must be
+# able to run binaries of this build triple.
+#
+# If `rustc` is present in path, this defaults to the host it was compiled for.
+# Otherwise, `x.py` will try to infer it from the output of `uname`.
+# If `uname` is not found in PATH, we assume this is `x86_64-pc-windows-msvc`.
+# This may be changed in the future.
+#build = "x86_64-unknown-linux-gnu" (as an example)
+
+# Which triples to produce a compiler toolchain for. Each of these triples will be bootstrapped from
+# the build triple themselves. In other words, this is the list of triples for which to build a
+# compiler that can RUN on that triple.
+#
+# Defaults to just the `build` triple.
+#host = [build.build] (list of triples)
+
+# Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of these triples will
+# be bootstrapped from the build triple themselves. In other words, this is the list of triples for
+# which to build a library that can CROSS-COMPILE to that triple.
+#
+# Defaults to `host`. If you set this explicitly, you likely want to add all
+# host triples to this list as well in order for those host toolchains to be
+# able to compile programs for their native target.
+#target = build.host (list of triples)
+
+# Use this directory to store build artifacts. Paths are relative to the current directory, not to
+# the root of the repository.
+#build-dir = "build"
+
+# Instead of downloading the src/stage0 version of Cargo specified, use
+# this Cargo binary instead to build all Rust code
+# If you set this, you likely want to set `rustc` as well.
+#cargo = "/path/to/cargo"
+
+# Instead of downloading the src/stage0 version of the compiler
+# specified, use this rustc binary instead as the stage0 snapshot compiler.
+# If you set this, you likely want to set `cargo` as well.
+#rustc = "/path/to/rustc"
+
+# Instead of downloading the src/stage0 version of rustfmt specified,
+# use this rustfmt binary instead as the stage0 snapshot rustfmt.
+#rustfmt = "/path/to/rustfmt"
+
+# Instead of downloading the src/stage0 version of cargo-clippy specified,
+# use this cargo-clippy binary instead as the stage0 snapshot cargo-clippy.
+#
+# Note that this option should be used with the same toolchain as the `rustc` option above.
+# Otherwise, clippy is likely to fail due to a toolchain conflict.
+#cargo-clippy = "/path/to/cargo-clippy"
+
+# Whether to build documentation by default. If false, rustdoc and
+# friends will still be compiled but they will not be used to generate any
+# documentation.
+#
+# You can still build documentation when this is disabled by explicitly passing paths,
+# e.g. `x doc library`.
+#docs = true
+
+# Flag to specify whether CSS, JavaScript, and HTML are minified when
+# docs are generated. JSON is always minified, because it's enormous,
+# and generated in already-minified form from the beginning.
+#docs-minification = true
+
+# Flag to specify whether private items should be included in the library docs.
+#library-docs-private-items = false
+
+# Indicate whether to build compiler documentation by default.
+# You can still build documentation when this is disabled by explicitly passing a path: `x doc compiler`.
+#compiler-docs = false
+
+# Indicate whether git submodules are managed and updated automatically.
+#submodules = true
+
+# The path to (or name of) the GDB executable to use. This is only used for
+# executing the debuginfo test suite.
+#gdb = "gdb"
+
+# The path to (or name of) the LLDB executable to use. This is only used for
+# executing the debuginfo test suite.
+#lldb = "lldb"
+
+# The node.js executable to use. Note that this is only used for the emscripten
+# target when running tests, otherwise this can be omitted.
+#nodejs = "node"
+
+# The npm executable to use. Note that this is used for rustdoc-gui tests,
+# otherwise this can be omitted.
+#
+# Under Windows this should be `npm.cmd` or path to it (verified on nodejs v18.06), or
+# error will be emitted.
+#npm = "npm"
+
+# Python interpreter to use for various tasks throughout the build, notably
+# rustdoc tests, the lldb python interpreter, and some dist bits and pieces.
+#
+# Defaults to the Python interpreter used to execute x.py.
+#python = "python"
+
+# The path to the REUSE executable to use. Note that REUSE is not required in
+# most cases, as our tooling relies on a cached (and shrunk) copy of the
+# REUSE output present in the git repository and in our source tarballs.
+#
+# REUSE is only needed if your changes caused the overall licensing of the
+# repository to change, and the cached copy has to be regenerated.
+#
+# Defaults to the "reuse" command in the system path.
+#reuse = "reuse"
+
+# Force Cargo to check that Cargo.lock describes the precise dependency
+# set that all the Cargo.toml files create, instead of updating it.
+#locked-deps = false
+
+# Indicate whether the vendored sources are used for Rust dependencies or not.
+#
+# Vendoring requires additional setup. We recommend using the pre-generated source tarballs if you
+# want to use vendoring. See https://forge.rust-lang.org/infra/other-installation-methods.html#source-code.
+#vendor = if "is a tarball source" && "vendor" dir exists && ".cargo/config.toml" file exists { true } else { false }
+
+# Typically the build system will build the Rust compiler twice. The second
+# compiler, however, will simply use its own libraries to link against. If you
+# would rather to perform a full bootstrap, compiling the compiler three times,
+# then you can set this option to true.
+#
+# This is only useful for verifying that rustc generates reproducible builds.
+#full-bootstrap = false
+
+# Set the bootstrap/download cache path. It is useful when building rust
+# repeatedly in a CI environment.
+#bootstrap-cache-path = /path/to/shared/cache
+
+# Enable a build of the extended Rust tool set which is not only the compiler
+# but also tools such as Cargo. This will also produce "combined installers"
+# which are used to install Rust and Cargo together.
+# The `tools` (check `bootstrap.example.toml` to see its default value) option specifies
+# which tools should be built if `extended = true`.
+#
+# This is disabled by default.
+#extended = false
+
+# Set of tools to be included in the installation.
+#
+# If `extended = false`, the only one of these built by default is rustdoc.
+#
+# If `extended = true`, they are all included.
+#
+# If any enabled tool fails to build, the installation fails.
+#tools = [
+#    "cargo",
+#    "clippy",
+#    "rustdoc",
+#    "rustfmt",
+#    "rust-analyzer",
+#    "rust-analyzer-proc-macro-srv",
+#    "analysis",
+#    "src",
+#    "wasm-component-ld",
+#    "miri", "cargo-miri" # for dev/nightly channels
+#]
+
+# Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose, 3 == print environment variables on each rustc invocation
+#verbose = 0
+
+# Build the sanitizer runtimes
+#sanitizers = false
+
+# Build the profiler runtime (required when compiling with options that depend
+# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`).
+#profiler = false
+
+# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics.
+# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
+# sources are available.
+#
+# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
+# order to run `x check`.
+#optimized-compiler-builtins = if rust.channel == "dev" { false } else { true }
+
+# Indicates whether the native libraries linked into Cargo will be statically
+# linked or not.
+#cargo-native-static = false
+
+# Run the build with low priority, by setting the process group's "nice" value
+# to +10 on Unix platforms, and by using a "low priority" job object on Windows.
+#low-priority = false
+
+# Arguments passed to the `./configure` script, used during distcheck. You
+# probably won't fill this in but rather it's filled in by the `./configure`
+# script. Useful for debugging.
+#configure-args = []
+
+# Indicates that a local rebuild is occurring instead of a full bootstrap,
+# essentially skipping stage0 as the local compiler is recompiling itself again.
+# Useful for modifying only the stage2 compiler without having to pass `--keep-stage 0` each time.
+#local-rebuild = false
+
+# Print out how long each bootstrap step took (mostly intended for CI and
+# tracking over time)
+#print-step-timings = false
+
+# Print out resource usage data for each bootstrap step, as defined by the Unix
+# struct rusage. (Note that this setting is completely unstable: the data it
+# captures, what platforms it supports, the format of its associated output, and
+# this setting's very existence, are all subject to change.)
+#print-step-rusage = false
+
+# Always patch binaries for usage with Nix toolchains. If `true` then binaries
+# will be patched unconditionally. If `false` or unset, binaries will be patched
+# only if the current distribution is NixOS. This option is useful when using
+# a Nix toolchain on non-NixOS distributions.
+#patch-binaries-for-nix = false
+
+# 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
+
+# Specify the location of the Android NDK. Used when targeting Android.
+#android-ndk = "/path/to/android-ndk-r26d"
+
+# Number of parallel jobs to be used for building and testing. If set to `0` or
+# omitted, it will be automatically determined. This is the `-j`/`--jobs` flag
+# passed to cargo invocations.
+#jobs = 0
+
+# What custom diff tool to use for displaying compiletest tests.
+#compiletest-diff-tool = <none>
+
+# Whether to use the precompiled stage0 libtest with compiletest.
+#compiletest-use-stage0-libtest = true
+
+# Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
+# Set to `true` to use the first `ccache` in PATH, or set an absolute path to use
+# a specific version.
+#ccache = false
+
+# List of paths to exclude from the build and test processes.
+# For example, exclude = ["tests/ui", "src/tools/tidy"].
+#exclude = []
+
+# =============================================================================
+# General install configuration options
+# =============================================================================
+[install]
+
+# Where to install the generated toolchain. Must be an absolute path.
+#prefix = "/usr/local"
+
+# Where to install system configuration files.
+# If this is a relative path, it will get installed in `prefix` above
+#sysconfdir = "/etc"
+
+# Where to install documentation in `prefix` above
+#docdir = "share/doc/rust"
+
+# Where to install binaries in `prefix` above
+#bindir = "bin"
+
+# Where to install libraries in `prefix` above
+#libdir = "lib"
+
+# Where to install man pages in `prefix` above
+#mandir = "share/man"
+
+# Where to install data in `prefix` above
+#datadir = "share"
+
+# =============================================================================
+# Options for compiling Rust code itself
+# =============================================================================
+[rust]
+
+# Whether or not to optimize when compiling the compiler and standard library,
+# and what level of optimization to use.
+# WARNING: Building with optimize = false is NOT SUPPORTED. Due to bootstrapping,
+# building without optimizations takes much longer than optimizing. Further, some platforms
+# fail to build without this optimization (c.f. #65352).
+# The valid options are:
+# true - Enable optimizations.
+# false - Disable optimizations.
+# 0 - Disable optimizations.
+# 1 - Basic optimizations.
+# 2 - Some optimizations.
+# 3 - All optimizations.
+# "s" - Optimize for binary size.
+# "z" - Optimize for binary size, but also turn off loop vectorization.
+#optimize = true
+
+# Indicates that the build should be configured for debugging Rust. A
+# `debug`-enabled compiler and standard library will be somewhat
+# slower (due to e.g. checking of debug assertions) but should remain
+# usable.
+#
+# Note: If this value is set to `true`, it will affect a number of
+#       configuration options below as well, if they have been left
+#       unconfigured in this file.
+#
+# Note: changes to the `debug` setting do *not* affect `optimize`
+#       above. In theory, a "maximally debuggable" environment would
+#       set `optimize` to `false` above to assist the introspection
+#       facilities of debuggers like lldb and gdb. To recreate such an
+#       environment, explicitly set `optimize` to `false` and `debug`
+#       to `true`. In practice, everyone leaves `optimize` set to
+#       `true`, because an unoptimized rustc with debugging
+#       enabled becomes *unusably slow* (e.g. rust-lang/rust#24840
+#       reported a 25x slowdown) and bootstrapping the supposed
+#       "maximally debuggable" environment (notably libstd) takes
+#       hours to build.
+#
+#debug = false
+
+# Whether to download the stage 1 and 2 compilers from CI. This is useful if you
+# are working on tools, doc-comments, or library (you will be able to build the
+# standard library without needing to build the compiler).
+#
+# Set this to "if-unchanged" if you are working on `src/tools`, `tests` or
+# `library` (on CI, `library` changes triggers in-tree compiler build) to speed
+# up the build process if you don't need to build a compiler from the latest
+# commit from `master`.
+#
+# Set this to `true` to always download or `false` to always use the in-tree
+# compiler.
+#download-rustc = false
+
+# Number of codegen units to use for each compiler invocation. A value of 0
+# means "the number of cores on this machine", and 1+ is passed through to the
+# compiler.
+#
+# Uses the rustc defaults: https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units
+#codegen-units = if incremental { 256 } else { 16 }
+
+# Sets the number of codegen units to build the standard library with,
+# regardless of what the codegen-unit setting for the rest of the compiler is.
+# NOTE: building with anything other than 1 is known to occasionally have bugs.
+#codegen-units-std = codegen-units
+
+# Whether or not debug assertions are enabled for the compiler and standard library.
+# These can help find bugs at the cost of a small runtime slowdown.
+#
+# Defaults to rust.debug value
+#debug-assertions = rust.debug (boolean)
+
+# Whether or not debug assertions are enabled for the standard library.
+# Overrides the `debug-assertions` option, if defined.
+#
+# Defaults to rust.debug-assertions value
+#debug-assertions-std = rust.debug-assertions (boolean)
+
+# Whether or not to leave debug! and trace! calls in the rust binary.
+#
+# Defaults to rust.debug-assertions value
+#
+# If you see a message from `tracing` saying "some trace filter directives would enable traces that
+# are disabled statically" because `max_level_info` is enabled, set this value to `true`.
+#debug-logging = rust.debug-assertions (boolean)
+
+# Whether or not to build rustc, tools and the libraries with randomized type layout
+#randomize-layout = false
+
+# Whether or not overflow checks are enabled for the compiler and standard
+# library.
+#
+# Defaults to rust.debug value
+#overflow-checks = rust.debug (boolean)
+
+# Whether or not overflow checks are enabled for the standard library.
+# Overrides the `overflow-checks` option, if defined.
+#
+# Defaults to rust.overflow-checks value
+#overflow-checks-std = rust.overflow-checks (boolean)
+
+# Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`.
+# See https://doc.rust-lang.org/rustc/codegen-options/index.html#debuginfo for available options.
+#
+# Can be overridden for specific subsets of Rust code (rustc, std or tools).
+# Debuginfo for tests run with compiletest is not controlled by this option
+# and needs to be enabled separately with `debuginfo-level-tests`.
+#
+# Note that debuginfo-level = 2 generates several gigabytes of debuginfo
+# and will slow down the linking process significantly.
+#debuginfo-level = if rust.debug { 1 } else { 0 }
+
+# Debuginfo level for the compiler.
+#debuginfo-level-rustc = rust.debuginfo-level
+
+# Debuginfo level for the standard library.
+#debuginfo-level-std = rust.debuginfo-level
+
+# Debuginfo level for the tools.
+#debuginfo-level-tools = rust.debuginfo-level
+
+# Debuginfo level for the test suites run with compiletest.
+# FIXME(#61117): Some tests fail when this option is enabled.
+#debuginfo-level-tests = 0
+
+# Should rustc and the standard library be built with split debuginfo? Default
+# is platform dependent.
+#
+# This field is deprecated, use `target.<triple>.split-debuginfo` instead.
+#
+# The value specified here is only used when targeting the `build.build` triple,
+# and is overridden by `target.<triple>.split-debuginfo` if specified.
+#
+#split-debuginfo = see target.<triple>.split-debuginfo
+
+# Whether or not `panic!`s generate backtraces (RUST_BACKTRACE)
+#backtrace = true
+
+# Whether to always use incremental compilation when building rustc
+#incremental = false
+
+# The default linker that will be hard-coded into the generated
+# compiler for targets that don't specify a default linker explicitly
+# in their target specifications.  Note that this is not the linker
+# used to link said compiler. It can also be set per-target (via the
+# `[target.<triple>]` block), which may be useful in a cross-compilation
+# setting.
+#
+# See https://doc.rust-lang.org/rustc/codegen-options/index.html#linker for more information.
+#default-linker = <none> (path)
+
+# The "channel" for the Rust build to produce. The stable/beta channels only
+# allow using stable features, whereas the nightly and dev channels allow using
+# nightly features.
+#
+# You can set the channel to "auto-detect" to load the channel name from `src/ci/channel`.
+#
+# If using tarball sources, default value is "auto-detect", otherwise, it's "dev".
+#channel = if "is a tarball source" { "auto-detect" } else { "dev" }
+
+# The root location of the musl installation directory. The library directory
+# will also need to contain libunwind.a for an unwinding implementation. Note
+# that this option only makes sense for musl targets that produce statically
+# linked binaries.
+#
+# Defaults to /usr on musl hosts. Has no default otherwise.
+#musl-root = <platform specific> (path)
+
+# By default the `rustc` executable is built with `-Wl,-rpath` flags on Unix
+# platforms to ensure that the compiler is usable by default from the build
+# directory (as it links to a number of dynamic libraries). This may not be
+# desired in distributions, for example.
+#rpath = true
+
+# Indicates whether symbols should be stripped using `-Cstrip=symbols`.
+#strip = false
+
+# Forces frame pointers to be used with `-Cforce-frame-pointers`.
+# This can be helpful for profiling at a small performance cost.
+#frame-pointers = false
+
+# Indicates whether stack protectors should be used
+# via the unstable option `-Zstack-protector`.
+#
+# Valid options are : `none`(default),`basic`,`strong`, or `all`.
+# `strong` and `basic` options may be buggy and are not recommended, see rust-lang/rust#114903.
+#stack-protector = "none"
+
+# Prints each test name as it is executed, to help debug issues in the test harness itself.
+#verbose-tests = if is_verbose { true } else { false }
+
+# Flag indicating whether tests are compiled with optimizations (the -O flag).
+#optimize-tests = true
+
+# Flag indicating whether codegen tests will be run or not. If you get an error
+# saying that the FileCheck executable is missing, you may want to disable this.
+# Also see the target's llvm-filecheck option.
+#codegen-tests = true
+
+# Flag indicating whether git info will be retrieved from .git automatically.
+# Having the git information can cause a lot of rebuilds during development.
+#omit-git-hash = if rust.channel == "dev" { true } else { false }
+
+# Whether to create a source tarball by default when running `x dist`.
+#
+# You can still build a source tarball when this is disabled by explicitly passing `x dist rustc-src`.
+#dist-src = true
+
+# After building or testing an optional component (e.g. the nomicon or reference), append the
+# result (broken, compiling, testing) into this JSON file.
+#save-toolstates = <none> (path)
+
+# This is an array of the codegen backends that will be compiled for the rustc
+# that's being compiled. The default is to only build the LLVM codegen backend,
+# and currently the only standard options supported are `"llvm"`, `"cranelift"`
+# and `"gcc"`. The first backend in this list will be used as default by rustc
+# when no explicit backend is specified.
+#codegen-backends = ["llvm"]
+
+# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and
+# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be
+# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from
+# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will
+# make this default to false.
+#lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true
+
+# Indicates whether LLD will be used to link Rust crates during bootstrap on
+# supported platforms.
+# If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH
+# will be used.
+# If set to `"self-contained"`, rust-lld from the snapshot compiler will be used.
+#
+# On MSVC, LLD will not be used if we're cross linking.
+#
+# Explicitly setting the linker for a target will override this option when targeting MSVC.
+#use-lld = false
+
+# Indicates whether some LLVM tools, like llvm-objdump, will be made available in the
+# sysroot.
+#llvm-tools = true
+
+# Indicates whether the `self-contained` llvm-bitcode-linker, will be made available
+# in the sysroot. It is required for running nvptx tests.
+#llvm-bitcode-linker = false
+
+# Whether to deny warnings in crates
+#deny-warnings = true
+
+# Print backtrace on internal compiler errors during bootstrap
+#backtrace-on-ice = false
+
+# Whether to verify generated LLVM IR
+#verify-llvm-ir = false
+
+# Compile the compiler with a non-default ThinLTO import limit. This import
+# limit controls the maximum size of functions imported by ThinLTO. Decreasing
+# will make code compile faster at the expense of lower runtime performance.
+#thin-lto-import-instr-limit = if incremental { 10 } else { LLVM default (currently 100) }
+
+# Map debuginfo paths to `/rust/$sha/...`.
+# Useful for reproducible builds. Generally only set for releases
+#remap-debuginfo = false
+
+# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
+# This option is only tested on Linux and OSX. It can also be configured per-target in the
+# [target.<tuple>] section.
+#jemalloc = false
+
+# Run tests in various test suites with the "nll compare mode" in addition to
+# running the tests in normal mode. Largely only used on CI and during local
+# development of NLL
+#test-compare-mode = false
+
+# Global default for llvm-libunwind for all targets. See the target-specific
+# documentation for llvm-libunwind below. Note that the target-specific
+# option will override this if set.
+#llvm-libunwind = 'no'
+
+# Enable Windows Control Flow Guard checks in the standard library.
+# This only applies from stage 1 onwards, and only for Windows targets.
+#control-flow-guard = false
+
+# Enable Windows EHCont Guard checks in the standard library.
+# This only applies from stage 1 onwards, and only for Windows targets.
+#ehcont-guard = false
+
+# Enable symbol-mangling-version v0. This can be helpful when profiling rustc,
+# as generics will be preserved in symbols (rather than erased into opaque T).
+# When no setting is given, the new scheme will be used when compiling the
+# compiler and its tools and the legacy scheme will be used when compiling the
+# standard library.
+# If an explicit setting is given, it will be used for all parts of the codebase.
+#new-symbol-mangling = true|false (see comment)
+
+# Select LTO mode that will be used for compiling rustc. By default, thin local LTO
+# (LTO within a single crate) is used (like for any Rust crate). You can also select
+# "thin" or "fat" to apply Thin/Fat LTO to the `rustc_driver` dylib, or "off" to disable
+# LTO entirely.
+#lto = "thin-local"
+
+# Build compiler with the optimization enabled and -Zvalidate-mir, currently only for `std`
+#validate-mir-opts = 3
+
+# Configure `std` features used during bootstrap.
+#
+# Default features will be expanded in the following cases:
+#  - If `rust.llvm-libunwind` or `target.llvm-libunwind` is enabled:
+#    - "llvm-libunwind" will be added for in-tree LLVM builds.
+#    - "system-llvm-libunwind" will be added for system LLVM builds.
+#  - If `rust.backtrace` is enabled, "backtrace" will be added.
+#  - If `rust.profiler` or `target.profiler` is enabled, "profiler" will be added.
+#  - If building for a zkvm target, "compiler-builtins-mem" will be added.
+#
+# Since libstd also builds libcore and liballoc as dependencies and all their features are mirrored
+# as libstd features, this option can also be used to configure features such as optimize_for_size.
+#std-features = ["panic_unwind"]
+
+# =============================================================================
+# Options for specific targets
+#
+# Each of the following options is scoped to the specific target triple in
+# question and is used for determining how to compile each target.
+# =============================================================================
+[target.x86_64-unknown-linux-gnu]
+
+# C compiler to be used to compile C code. Note that the
+# default value is platform specific, and if not specified it may also depend on
+# what platform is crossing to what platform.
+# See `src/bootstrap/src/utils/cc_detect.rs` for details.
+#cc = "cc" (path)
+
+# C++ compiler to be used to compile C++ code (e.g. LLVM and our LLVM shims).
+# This is only used for host targets.
+# See `src/bootstrap/src/utils/cc_detect.rs` for details.
+#cxx = "c++" (path)
+
+# Archiver to be used to assemble static libraries compiled from C/C++ code.
+# Note: an absolute path should be used, otherwise LLVM build will break.
+#ar = "ar" (path)
+
+# Ranlib to be used to assemble static libraries compiled from C/C++ code.
+# Note: an absolute path should be used, otherwise LLVM build will break.
+#ranlib = "ranlib" (path)
+
+# Linker to be used to bootstrap Rust code. Note that the
+# default value is platform specific, and if not specified it may also depend on
+# what platform is crossing to what platform.
+# Setting this will override the `use-lld` option for Rust code when targeting MSVC.
+#linker = "cc" (path)
+
+# Should rustc and the standard library be built with split debuginfo? Default
+# is platform dependent.
+#
+# Valid values are the same as those accepted by `-C split-debuginfo`
+# (`off`/`unpacked`/`packed`).
+#
+# On Linux, split debuginfo is disabled by default.
+#
+# On Apple platforms, unpacked split debuginfo is used by default. Unpacked
+# debuginfo does not run `dsymutil`, which packages debuginfo from disparate
+# object files into a single `.dSYM` file. `dsymutil` adds time to builds for
+# no clear benefit, and also makes it more difficult for debuggers to find
+# debug info. The compiler currently defaults to running `dsymutil` to preserve
+# its historical default, but when compiling the compiler itself, we skip it by
+# default since we know it's safe to do so in that case.
+#
+# On Windows platforms, packed debuginfo is the only supported option,
+# producing a `.pdb` file.
+#split-debuginfo = if linux { off } else if windows { packed } else if apple { unpacked }
+
+# Path to the `llvm-config` binary of the installation of a custom LLVM to link
+# against. Note that if this is specified we don't compile LLVM at all for this
+# target.
+#llvm-config = <none> (path)
+
+# Override detection of whether this is a Rust-patched LLVM. This would be used
+# in conjunction with either an llvm-config or build.submodules = false.
+#llvm-has-rust-patches = if llvm-config { false } else { true }
+
+# Normally the build system can find LLVM's FileCheck utility, but if
+# not, you can specify an explicit file name for it.
+#llvm-filecheck = "/path/to/llvm-version/bin/FileCheck"
+
+# Use LLVM libunwind as the implementation for Rust's unwinder.
+# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false).
+# This option only applies for Linux and Fuchsia targets.
+# On Linux target, if crt-static is not enabled, 'no' means dynamic link to
+# `libgcc_s.so`, 'in-tree' means static link to the in-tree build of llvm libunwind
+# and 'system' means dynamic link to `libunwind.so`. If crt-static is enabled,
+# the behavior is depend on the libc. On musl target, 'no' and 'in-tree' both
+# means static link to the in-tree build of llvm libunwind, and 'system' means
+# static link to `libunwind.a` provided by system. Due to the limitation of glibc,
+# it must link to `libgcc_eh.a` to get a working output, and this option have no effect.
+#llvm-libunwind = 'no' if Linux, 'in-tree' if Fuchsia
+
+# Build the sanitizer runtimes for this target.
+# This option will override the same option under [build] section.
+#sanitizers = build.sanitizers (bool)
+
+# When true, build the profiler runtime for this target (required when compiling
+# with options that depend on this runtime, such as `-C profile-generate` or
+# `-C instrument-coverage`). This may also be given a path to an existing build
+# of the profiling runtime library from LLVM's compiler-rt.
+# This option will override the same option under [build] section.
+#profiler = build.profiler (bool)
+
+# This option supports enable `rpath` in each target independently,
+# and will override the same option under [rust] section. It only works on Unix platforms
+#rpath = rust.rpath (bool)
+
+# Force static or dynamic linkage of the standard library for this target. If
+# this target is a host for rustc, this will also affect the linkage of the
+# compiler itself. This is useful for building rustc on targets that normally
+# only use static libraries. If unset, the target's default linkage is used.
+#crt-static = <platform-specific> (bool)
+
+# The root location of the musl installation directory. The library directory
+# will also need to contain libunwind.a for an unwinding implementation. Note
+# that this option only makes sense for musl targets that produce statically
+# linked binaries.
+#musl-root = build.musl-root (path)
+
+# The full path to the musl libdir.
+#musl-libdir = musl-root/lib
+
+# The root location of the `wasm32-wasip1` sysroot. Only used for WASI
+# related targets. Make sure to create a `[target.wasm32-wasip1]`
+# section and move this field there (or equivalent for the target being built).
+#wasi-root = <none> (path)
+
+# Used in testing for configuring where the QEMU images are located, you
+# probably don't want to use this.
+#qemu-rootfs = <none> (path)
+
+# Skip building the `std` library for this target. Enabled by default for
+# target triples containing `-none`, `nvptx`, `switch`, or `-uefi`.
+#no-std = <platform-specific> (bool)
+
+# This is an array of the codegen backends that will be
+# compiled for this target, overriding the global rust.codegen-backends option.
+# See that option for more info.
+#codegen-backends = rust.codegen-backends (array)
+
+# This is a "runner" to pass to `compiletest` when executing tests. Tests will
+# execute this tool where the binary-to-test is passed as an argument. Can
+# be useful for situations such as when WebAssembly is being tested and a
+# runtime needs to be configured. This value is similar to
+# Cargo's `CARGO_$target_RUNNER` configuration.
+#
+# This configuration is a space-separated list of arguments so `foo bar` would
+# execute the program `foo` with the first argument as `bar` and the second
+# argument as the test binary.
+#runner = <none> (string)
+
+# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics
+# on this target.
+# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
+# sources are available.
+#
+# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
+# order to run `x check`.
+#optimized-compiler-builtins = build.optimized-compiler-builtins (bool)
+
+# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
+# This overrides the global `rust.jemalloc` option. See that option for more info.
+#jemalloc = rust.jemalloc (bool)
+
+# =============================================================================
+# Distribution options
+#
+# These options are related to distribution, mostly for the Rust project itself.
+# You probably won't need to concern yourself with any of these options
+# =============================================================================
+[dist]
+
+# This is the folder of artifacts that the build system will sign. All files in
+# this directory will be signed with the default gpg key using the system `gpg`
+# binary. The `asc` and `sha256` files will all be output into the standard dist
+# output folder (currently `build/dist`)
+#
+# This folder should be populated ahead of time before the build system is
+# invoked.
+#sign-folder = <none> (path)
+
+# The remote address that all artifacts will eventually be uploaded to. The
+# build system generates manifests which will point to these urls, and for the
+# manifests to be correct they'll have to have the right URLs encoded.
+#
+# Note that this address should not contain a trailing slash as file names will
+# be appended to it.
+#upload-addr = <none> (URL)
+
+# Whether to build a plain source tarball to upload
+# We disable that on Windows not to override the one already uploaded on S3
+# as the one built on Windows will contain backslashes in paths causing problems
+# on linux
+#src-tarball = true
+
+# List of compression formats to use when generating dist tarballs. The list of
+# formats is provided to rust-installer, which must support all of them.
+#
+# This list must be non-empty.
+#compression-formats = ["gz", "xz"]
+
+# How much time should be spent compressing the tarballs. The better the
+# compression profile, the longer compression will take.
+#
+# Available options: fast, balanced, best
+#compression-profile = "fast"
+
+# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain.
+# Only applies when the host or target is pc-windows-gnu.
+#include-mingw-linker = true
+
+# Whether to vendor dependencies for the dist tarball.
+#vendor = if "is a tarball source" || "is a git repository" { true } else { false }
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml
index d24b630516a7..f4caa3ef769d 100644
--- a/compiler/rustc/Cargo.toml
+++ b/compiler/rustc/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustc-main"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs
index a55a63a7bf17..ca1bb59e59d6 100644
--- a/compiler/rustc/src/main.rs
+++ b/compiler/rustc/src/main.rs
@@ -65,7 +65,7 @@ fn main() {
         // linking, so we need to explicitly depend on the function.
         #[cfg(target_os = "macos")]
         {
-            extern "C" {
+            unsafe extern "C" {
                 fn _rjem_je_zone_register();
             }
 
diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml
index 3acd25e54611..5f9afc46a1ac 100644
--- a/compiler/rustc_abi/Cargo.toml
+++ b/compiler/rustc_abi/Cargo.toml
@@ -1,18 +1,18 @@
 [package]
 name = "rustc_abi"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-rand = { version = "0.8.4", default-features = false, optional = true }
-rand_xoshiro = { version = "0.6.0", optional = true }
-rustc_data_structures = { path = "../rustc_data_structures", optional = true  }
-rustc_feature = { path = "../rustc_feature", optional = true }
+rand = { version = "0.9.0", default-features = false, optional = true }
+rand_xoshiro = { version = "0.7.0", optional = true }
+rustc_data_structures = { path = "../rustc_data_structures", optional = true }
+rustc_hashes = { path = "../rustc_hashes" }
 rustc_index = { path = "../rustc_index", default-features = false }
 rustc_macros = { path = "../rustc_macros", optional = true }
-rustc_serialize = { path = "../rustc_serialize", optional = true  }
+rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_span = { path = "../rustc_span", optional = true }
 tracing = "0.1"
 # tidy-alphabetical-end
@@ -24,7 +24,6 @@ default = ["nightly", "randomize"]
 # without depending on rustc_data_structures, rustc_macros and rustc_serialize
 nightly = [
     "dep:rustc_data_structures",
-    "dep:rustc_feature",
     "dep:rustc_macros",
     "dep:rustc_serialize",
     "dep:rustc_span",
diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs
index 400395f99ff0..a21e1aee9b08 100644
--- a/compiler/rustc_abi/src/callconv.rs
+++ b/compiler/rustc_abi/src/callconv.rs
@@ -1,73 +1,9 @@
-mod abi {
-    pub(crate) use crate::Primitive::*;
-    pub(crate) use crate::Variants;
-}
-
-#[cfg(feature = "nightly")]
-use rustc_macros::HashStable_Generic;
-
-use crate::{Align, HasDataLayout, Size};
 #[cfg(feature = "nightly")]
-use crate::{BackendRepr, FieldsShape, TyAbiInterface, TyAndLayout};
-
-#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum RegKind {
-    Integer,
-    Float,
-    Vector,
-}
-
-#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub struct Reg {
-    pub kind: RegKind,
-    pub size: Size,
-}
-
-macro_rules! reg_ctor {
-    ($name:ident, $kind:ident, $bits:expr) => {
-        pub fn $name() -> Reg {
-            Reg { kind: RegKind::$kind, size: Size::from_bits($bits) }
-        }
-    };
-}
+use crate::{BackendRepr, FieldsShape, Primitive, Size, TyAbiInterface, TyAndLayout, Variants};
 
-impl Reg {
-    reg_ctor!(i8, Integer, 8);
-    reg_ctor!(i16, Integer, 16);
-    reg_ctor!(i32, Integer, 32);
-    reg_ctor!(i64, Integer, 64);
-    reg_ctor!(i128, Integer, 128);
-
-    reg_ctor!(f32, Float, 32);
-    reg_ctor!(f64, Float, 64);
-}
+mod reg;
 
-impl Reg {
-    pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
-        let dl = cx.data_layout();
-        match self.kind {
-            RegKind::Integer => match self.size.bits() {
-                1 => dl.i1_align.abi,
-                2..=8 => dl.i8_align.abi,
-                9..=16 => dl.i16_align.abi,
-                17..=32 => dl.i32_align.abi,
-                33..=64 => dl.i64_align.abi,
-                65..=128 => dl.i128_align.abi,
-                _ => panic!("unsupported integer: {self:?}"),
-            },
-            RegKind::Float => match self.size.bits() {
-                16 => dl.f16_align.abi,
-                32 => dl.f32_align.abi,
-                64 => dl.f64_align.abi,
-                128 => dl.f128_align.abi,
-                _ => panic!("unsupported float: {self:?}"),
-            },
-            RegKind::Vector => dl.vector_align(self.size).abi,
-        }
-    }
-}
+pub use reg::{Reg, RegKind};
 
 /// Return value from the `homogeneous_aggregate` test function.
 #[derive(Copy, Clone, Debug)]
@@ -129,18 +65,16 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         Ty: TyAbiInterface<'a, C> + Copy,
     {
         match self.backend_repr {
-            BackendRepr::Uninhabited => Err(Heterogeneous),
-
             // The primitive for this algorithm.
             BackendRepr::Scalar(scalar) => {
                 let kind = match scalar.primitive() {
-                    abi::Int(..) | abi::Pointer(_) => RegKind::Integer,
-                    abi::Float(_) => RegKind::Float,
+                    Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer,
+                    Primitive::Float(_) => RegKind::Float,
                 };
                 Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
             }
 
-            BackendRepr::Vector { .. } => {
+            BackendRepr::SimdVector { .. } => {
                 assert!(!self.is_zst());
                 Ok(HomogeneousAggregate::Homogeneous(Reg {
                     kind: RegKind::Vector,
@@ -206,8 +140,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
                 let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
 
                 match &self.variants {
-                    abi::Variants::Single { .. } | abi::Variants::Empty => {}
-                    abi::Variants::Multiple { variants, .. } => {
+                    Variants::Single { .. } | Variants::Empty => {}
+                    Variants::Multiple { variants, .. } => {
                         // Treat enum variants like union members.
                         // HACK(eddyb) pretend the `enum` field (discriminant)
                         // is at the start of every variant (otherwise the gap
diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs
new file mode 100644
index 000000000000..8cf140dbaad4
--- /dev/null
+++ b/compiler/rustc_abi/src/callconv/reg.rs
@@ -0,0 +1,63 @@
+#[cfg(feature = "nightly")]
+use rustc_macros::HashStable_Generic;
+
+use crate::{Align, HasDataLayout, Size};
+
+#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum RegKind {
+    Integer,
+    Float,
+    Vector,
+}
+
+#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct Reg {
+    pub kind: RegKind,
+    pub size: Size,
+}
+
+macro_rules! reg_ctor {
+    ($name:ident, $kind:ident, $bits:expr) => {
+        pub fn $name() -> Reg {
+            Reg { kind: RegKind::$kind, size: Size::from_bits($bits) }
+        }
+    };
+}
+
+impl Reg {
+    reg_ctor!(i8, Integer, 8);
+    reg_ctor!(i16, Integer, 16);
+    reg_ctor!(i32, Integer, 32);
+    reg_ctor!(i64, Integer, 64);
+    reg_ctor!(i128, Integer, 128);
+
+    reg_ctor!(f32, Float, 32);
+    reg_ctor!(f64, Float, 64);
+}
+
+impl Reg {
+    pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
+        let dl = cx.data_layout();
+        match self.kind {
+            RegKind::Integer => match self.size.bits() {
+                1 => dl.i1_align.abi,
+                2..=8 => dl.i8_align.abi,
+                9..=16 => dl.i16_align.abi,
+                17..=32 => dl.i32_align.abi,
+                33..=64 => dl.i64_align.abi,
+                65..=128 => dl.i128_align.abi,
+                _ => panic!("unsupported integer: {self:?}"),
+            },
+            RegKind::Float => match self.size.bits() {
+                16 => dl.f16_align.abi,
+                32 => dl.f32_align.abi,
+                64 => dl.f64_align.abi,
+                128 => dl.f128_align.abi,
+                _ => panic!("unsupported float: {self:?}"),
+            },
+            RegKind::Vector => dl.llvmlike_vector_align(self.size).abi,
+        }
+    }
+}
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
new file mode 100644
index 000000000000..55f4845d2167
--- /dev/null
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -0,0 +1,241 @@
+use std::cmp::Ordering;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+
+#[cfg(feature = "nightly")]
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
+#[cfg(feature = "nightly")]
+use rustc_macros::{Decodable, Encodable};
+
+#[cfg(test)]
+mod tests;
+
+use ExternAbi as Abi;
+
+#[derive(Clone, Copy, Debug)]
+#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
+pub enum ExternAbi {
+    // Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
+    // hashing tests. These are used in many places, so giving them stable values reduces test
+    // churn. The specific values are meaningless.
+    Rust,
+    C {
+        unwind: bool,
+    },
+    Cdecl {
+        unwind: bool,
+    },
+    Stdcall {
+        unwind: bool,
+    },
+    Fastcall {
+        unwind: bool,
+    },
+    Vectorcall {
+        unwind: bool,
+    },
+    Thiscall {
+        unwind: bool,
+    },
+    Aapcs {
+        unwind: bool,
+    },
+    Win64 {
+        unwind: bool,
+    },
+    SysV64 {
+        unwind: bool,
+    },
+    PtxKernel,
+    Msp430Interrupt,
+    X86Interrupt,
+    /// An entry-point function called by the GPU's host
+    // FIXME: should not be callable from Rust on GPU targets, is for host's use only
+    GpuKernel,
+    EfiApi,
+    AvrInterrupt,
+    AvrNonBlockingInterrupt,
+    CCmseNonSecureCall,
+    CCmseNonSecureEntry,
+    System {
+        unwind: bool,
+    },
+    RustCall,
+    /// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even
+    /// normally ABI-compatible Rust types can become ABI-incompatible with this ABI!
+    Unadjusted,
+    /// For things unlikely to be called, where reducing register pressure in
+    /// `extern "Rust"` callers is worth paying extra cost in the callee.
+    /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
+    RustCold,
+    RiscvInterruptM,
+    RiscvInterruptS,
+}
+
+macro_rules! abi_impls {
+    ($e_name:ident = {
+        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
+    }) => {
+        impl $e_name {
+            pub const ALL_VARIANTS: &[Self] = &[
+                $($e_name::$variant $({ unwind: $uw })*,)*
+            ];
+            pub const fn as_str(&self) -> &'static str {
+                match self {
+                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*
+                }
+            }
+        }
+
+        impl ::core::str::FromStr for $e_name {
+            type Err = AbiFromStrErr;
+            fn from_str(s: &str) -> Result<$e_name, Self::Err> {
+                match s {
+                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
+                    _ => Err(AbiFromStrErr::Unknown),
+                }
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum AbiFromStrErr {
+    Unknown,
+}
+
+abi_impls! {
+    ExternAbi = {
+            C { unwind: false } =><= "C",
+            CCmseNonSecureCall =><= "C-cmse-nonsecure-call",
+            CCmseNonSecureEntry =><= "C-cmse-nonsecure-entry",
+            C { unwind: true } =><= "C-unwind",
+            Rust =><= "Rust",
+            Aapcs { unwind: false } =><= "aapcs",
+            Aapcs { unwind: true } =><= "aapcs-unwind",
+            AvrInterrupt =><= "avr-interrupt",
+            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
+            Cdecl { unwind: false } =><= "cdecl",
+            Cdecl { unwind: true } =><= "cdecl-unwind",
+            EfiApi =><= "efiapi",
+            Fastcall { unwind: false } =><= "fastcall",
+            Fastcall { unwind: true } =><= "fastcall-unwind",
+            GpuKernel =><= "gpu-kernel",
+            Msp430Interrupt =><= "msp430-interrupt",
+            PtxKernel =><= "ptx-kernel",
+            RiscvInterruptM =><= "riscv-interrupt-m",
+            RiscvInterruptS =><= "riscv-interrupt-s",
+            RustCall =><= "rust-call",
+            RustCold =><= "rust-cold",
+            Stdcall { unwind: false } =><= "stdcall",
+            Stdcall { unwind: true } =><= "stdcall-unwind",
+            System { unwind: false } =><= "system",
+            System { unwind: true } =><= "system-unwind",
+            SysV64 { unwind: false } =><= "sysv64",
+            SysV64 { unwind: true } =><= "sysv64-unwind",
+            Thiscall { unwind: false } =><= "thiscall",
+            Thiscall { unwind: true } =><= "thiscall-unwind",
+            Unadjusted =><= "unadjusted",
+            Vectorcall { unwind: false } =><= "vectorcall",
+            Vectorcall { unwind: true } =><= "vectorcall-unwind",
+            Win64 { unwind: false } =><= "win64",
+            Win64 { unwind: true } =><= "win64-unwind",
+            X86Interrupt =><= "x86-interrupt",
+    }
+}
+
+impl Ord for ExternAbi {
+    fn cmp(&self, rhs: &Self) -> Ordering {
+        self.as_str().cmp(rhs.as_str())
+    }
+}
+
+impl PartialOrd for ExternAbi {
+    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
+        Some(self.cmp(rhs))
+    }
+}
+
+impl PartialEq for ExternAbi {
+    fn eq(&self, rhs: &Self) -> bool {
+        self.cmp(rhs) == Ordering::Equal
+    }
+}
+
+impl Eq for ExternAbi {}
+
+impl Hash for ExternAbi {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.as_str().hash(state);
+        // double-assurance of a prefix breaker
+        u32::from_be_bytes(*b"ABI\0").hash(state);
+    }
+}
+
+#[cfg(feature = "nightly")]
+impl<C> HashStable<C> for ExternAbi {
+    #[inline]
+    fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
+        Hash::hash(self, hasher);
+    }
+}
+
+#[cfg(feature = "nightly")]
+impl StableOrd for ExternAbi {
+    const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    // because each ABI is hashed like a string, there is no possible instability
+    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
+}
+
+impl ExternAbi {
+    /// An ABI "like Rust"
+    ///
+    /// These ABIs are fully controlled by the Rust compiler, which means they
+    /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`
+    /// - often diverge from the C ABI
+    /// - are subject to change between compiler versions
+    pub fn is_rustic_abi(self) -> bool {
+        use ExternAbi::*;
+        matches!(self, Rust | RustCall | RustCold)
+    }
+
+    pub fn supports_varargs(self) -> bool {
+        // * C and Cdecl obviously support varargs.
+        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
+        // * EfiApi is based on Win64 or C, so it also supports it.
+        //
+        // * Stdcall does not, because it would be impossible for the callee to clean
+        //   up the arguments. (callee doesn't know how many arguments are there)
+        // * Same for Fastcall, Vectorcall and Thiscall.
+        // * Other calling conventions are related to hardware or the compiler itself.
+        match self {
+            Self::C { .. }
+            | Self::Cdecl { .. }
+            | Self::Aapcs { .. }
+            | Self::Win64 { .. }
+            | Self::SysV64 { .. }
+            | Self::EfiApi => true,
+            _ => false,
+        }
+    }
+}
+
+pub fn all_names() -> Vec<&'static str> {
+    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
+}
+
+impl ExternAbi {
+    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.
+    pub const FALLBACK: Abi = Abi::C { unwind: false };
+
+    pub fn name(self) -> &'static str {
+        self.as_str()
+    }
+}
+
+impl fmt::Display for ExternAbi {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "\"{}\"", self.as_str())
+    }
+}
diff --git a/compiler/rustc_abi/src/extern_abi/mod.rs b/compiler/rustc_abi/src/extern_abi/mod.rs
deleted file mode 100644
index 130834d560f7..000000000000
--- a/compiler/rustc_abi/src/extern_abi/mod.rs
+++ /dev/null
@@ -1,347 +0,0 @@
-use std::fmt;
-
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
-use rustc_span::{Span, Symbol, sym};
-
-#[cfg(test)]
-mod tests;
-
-use ExternAbi as Abi;
-
-#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
-#[derive(HashStable_Generic, Encodable, Decodable)]
-pub enum ExternAbi {
-    // Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
-    // hashing tests. These are used in many places, so giving them stable values reduces test
-    // churn. The specific values are meaningless.
-    Rust,
-    C {
-        unwind: bool,
-    },
-    Cdecl {
-        unwind: bool,
-    },
-    Stdcall {
-        unwind: bool,
-    },
-    Fastcall {
-        unwind: bool,
-    },
-    Vectorcall {
-        unwind: bool,
-    },
-    Thiscall {
-        unwind: bool,
-    },
-    Aapcs {
-        unwind: bool,
-    },
-    Win64 {
-        unwind: bool,
-    },
-    SysV64 {
-        unwind: bool,
-    },
-    PtxKernel,
-    Msp430Interrupt,
-    X86Interrupt,
-    /// An entry-point function called by the GPU's host
-    // FIXME: should not be callable from Rust on GPU targets, is for host's use only
-    GpuKernel,
-    EfiApi,
-    AvrInterrupt,
-    AvrNonBlockingInterrupt,
-    CCmseNonSecureCall,
-    CCmseNonSecureEntry,
-    System {
-        unwind: bool,
-    },
-    RustIntrinsic,
-    RustCall,
-    /// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even
-    /// normally ABI-compatible Rust types can become ABI-incompatible with this ABI!
-    Unadjusted,
-    /// For things unlikely to be called, where reducing register pressure in
-    /// `extern "Rust"` callers is worth paying extra cost in the callee.
-    /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
-    RustCold,
-    RiscvInterruptM,
-    RiscvInterruptS,
-}
-
-impl Abi {
-    pub fn supports_varargs(self) -> bool {
-        // * C and Cdecl obviously support varargs.
-        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
-        // * EfiApi is based on Win64 or C, so it also supports it.
-        // * System falls back to C for functions with varargs.
-        //
-        // * Stdcall does not, because it would be impossible for the callee to clean
-        //   up the arguments. (callee doesn't know how many arguments are there)
-        // * Same for Fastcall, Vectorcall and Thiscall.
-        // * Other calling conventions are related to hardware or the compiler itself.
-        match self {
-            Self::C { .. }
-            | Self::Cdecl { .. }
-            | Self::System { .. }
-            | Self::Aapcs { .. }
-            | Self::Win64 { .. }
-            | Self::SysV64 { .. }
-            | Self::EfiApi => true,
-            _ => false,
-        }
-    }
-}
-
-#[derive(Copy, Clone)]
-pub struct AbiData {
-    abi: Abi,
-
-    /// Name of this ABI as we like it called.
-    name: &'static str,
-}
-
-#[allow(non_upper_case_globals)]
-const AbiDatas: &[AbiData] = &[
-    AbiData { abi: Abi::Rust, name: "Rust" },
-    AbiData { abi: Abi::C { unwind: false }, name: "C" },
-    AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" },
-    AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" },
-    AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" },
-    AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" },
-    AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" },
-    AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" },
-    AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" },
-    AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" },
-    AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" },
-    AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" },
-    AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" },
-    AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" },
-    AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" },
-    AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" },
-    AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" },
-    AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" },
-    AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" },
-    AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" },
-    AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" },
-    AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" },
-    AbiData { abi: Abi::GpuKernel, name: "gpu-kernel" },
-    AbiData { abi: Abi::EfiApi, name: "efiapi" },
-    AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt" },
-    AbiData { abi: Abi::AvrNonBlockingInterrupt, name: "avr-non-blocking-interrupt" },
-    AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call" },
-    AbiData { abi: Abi::CCmseNonSecureEntry, name: "C-cmse-nonsecure-entry" },
-    AbiData { abi: Abi::System { unwind: false }, name: "system" },
-    AbiData { abi: Abi::System { unwind: true }, name: "system-unwind" },
-    AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic" },
-    AbiData { abi: Abi::RustCall, name: "rust-call" },
-    AbiData { abi: Abi::Unadjusted, name: "unadjusted" },
-    AbiData { abi: Abi::RustCold, name: "rust-cold" },
-    AbiData { abi: Abi::RiscvInterruptM, name: "riscv-interrupt-m" },
-    AbiData { abi: Abi::RiscvInterruptS, name: "riscv-interrupt-s" },
-];
-
-#[derive(Copy, Clone, Debug)]
-pub enum AbiUnsupported {
-    Unrecognized,
-    Reason { explain: &'static str },
-}
-
-/// Returns the ABI with the given name (if any).
-pub fn lookup(name: &str) -> Result<Abi, AbiUnsupported> {
-    AbiDatas.iter().find(|abi_data| name == abi_data.name).map(|&x| x.abi).ok_or_else(|| match name {
-        "riscv-interrupt" => AbiUnsupported::Reason {
-            explain: "please use one of riscv-interrupt-m or riscv-interrupt-s for machine- or supervisor-level interrupts, respectively",
-        },
-        "riscv-interrupt-u" => AbiUnsupported::Reason {
-            explain: "user-mode interrupt handlers have been removed from LLVM pending standardization, see: https://reviews.llvm.org/D149314",
-        },
-        "wasm" => AbiUnsupported::Reason {
-            explain: "non-standard wasm ABI is no longer supported",
-        },
-
-        _ => AbiUnsupported::Unrecognized,
-
-    })
-}
-
-pub fn all_names() -> Vec<&'static str> {
-    AbiDatas.iter().map(|d| d.name).collect()
-}
-
-pub fn enabled_names(features: &rustc_feature::Features, span: Span) -> Vec<&'static str> {
-    AbiDatas
-        .iter()
-        .map(|d| d.name)
-        .filter(|name| is_enabled(features, span, name).is_ok())
-        .collect()
-}
-
-pub enum AbiDisabled {
-    Unstable { feature: Symbol, explain: &'static str },
-    Unrecognized,
-}
-
-pub fn is_enabled(
-    features: &rustc_feature::Features,
-    span: Span,
-    name: &str,
-) -> Result<(), AbiDisabled> {
-    let s = is_stable(name);
-    if let Err(AbiDisabled::Unstable { feature, .. }) = s {
-        if features.enabled(feature) || span.allows_unstable(feature) {
-            return Ok(());
-        }
-    }
-    s
-}
-
-/// Returns whether the ABI is stable to use.
-///
-/// Note that there is a separate check determining whether the ABI is even supported
-/// on the current target; see `fn is_abi_supported` in `rustc_target::spec`.
-pub fn is_stable(name: &str) -> Result<(), AbiDisabled> {
-    match name {
-        // Stable
-        "Rust" | "C" | "C-unwind" | "cdecl" | "cdecl-unwind" | "stdcall" | "stdcall-unwind"
-        | "fastcall" | "fastcall-unwind" | "aapcs" | "aapcs-unwind" | "win64" | "win64-unwind"
-        | "sysv64" | "sysv64-unwind" | "system" | "system-unwind" | "efiapi" | "thiscall"
-        | "thiscall-unwind" => Ok(()),
-        "rust-intrinsic" => Err(AbiDisabled::Unstable {
-            feature: sym::intrinsics,
-            explain: "intrinsics are subject to change",
-        }),
-        "vectorcall" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_vectorcall,
-            explain: "vectorcall is experimental and subject to change",
-        }),
-        "vectorcall-unwind" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_vectorcall,
-            explain: "vectorcall-unwind ABI is experimental and subject to change",
-        }),
-        "rust-call" => Err(AbiDisabled::Unstable {
-            feature: sym::unboxed_closures,
-            explain: "rust-call ABI is subject to change",
-        }),
-        "rust-cold" => Err(AbiDisabled::Unstable {
-            feature: sym::rust_cold_cc,
-            explain: "rust-cold is experimental and subject to change",
-        }),
-        "ptx-kernel" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_ptx,
-            explain: "PTX ABIs are experimental and subject to change",
-        }),
-        "unadjusted" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_unadjusted,
-            explain: "unadjusted ABI is an implementation detail and perma-unstable",
-        }),
-        "msp430-interrupt" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_msp430_interrupt,
-            explain: "msp430-interrupt ABI is experimental and subject to change",
-        }),
-        "x86-interrupt" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_x86_interrupt,
-            explain: "x86-interrupt ABI is experimental and subject to change",
-        }),
-        "gpu-kernel" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_gpu_kernel,
-            explain: "gpu-kernel ABI is experimental and subject to change",
-        }),
-        "avr-interrupt" | "avr-non-blocking-interrupt" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_avr_interrupt,
-            explain: "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change",
-        }),
-        "riscv-interrupt-m" | "riscv-interrupt-s" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_riscv_interrupt,
-            explain: "riscv-interrupt ABIs are experimental and subject to change",
-        }),
-        "C-cmse-nonsecure-call" => Err(AbiDisabled::Unstable {
-            feature: sym::abi_c_cmse_nonsecure_call,
-            explain: "C-cmse-nonsecure-call ABI is experimental and subject to change",
-        }),
-        "C-cmse-nonsecure-entry" => Err(AbiDisabled::Unstable {
-            feature: sym::cmse_nonsecure_entry,
-            explain: "C-cmse-nonsecure-entry ABI is experimental and subject to change",
-        }),
-        _ => Err(AbiDisabled::Unrecognized),
-    }
-}
-
-impl Abi {
-    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.
-    pub const FALLBACK: Abi = Abi::C { unwind: false };
-
-    #[inline]
-    pub fn index(self) -> usize {
-        // N.B., this ordering MUST match the AbiDatas array above.
-        // (This is ensured by the test indices_are_correct().)
-        use Abi::*;
-        let i = match self {
-            // Cross-platform ABIs
-            Rust => 0,
-            C { unwind: false } => 1,
-            C { unwind: true } => 2,
-            // Platform-specific ABIs
-            Cdecl { unwind: false } => 3,
-            Cdecl { unwind: true } => 4,
-            Stdcall { unwind: false } => 5,
-            Stdcall { unwind: true } => 6,
-            Fastcall { unwind: false } => 7,
-            Fastcall { unwind: true } => 8,
-            Vectorcall { unwind: false } => 9,
-            Vectorcall { unwind: true } => 10,
-            Thiscall { unwind: false } => 11,
-            Thiscall { unwind: true } => 12,
-            Aapcs { unwind: false } => 13,
-            Aapcs { unwind: true } => 14,
-            Win64 { unwind: false } => 15,
-            Win64 { unwind: true } => 16,
-            SysV64 { unwind: false } => 17,
-            SysV64 { unwind: true } => 18,
-            PtxKernel => 19,
-            Msp430Interrupt => 20,
-            X86Interrupt => 21,
-            GpuKernel => 22,
-            EfiApi => 23,
-            AvrInterrupt => 24,
-            AvrNonBlockingInterrupt => 25,
-            CCmseNonSecureCall => 26,
-            CCmseNonSecureEntry => 27,
-            // Cross-platform ABIs
-            System { unwind: false } => 28,
-            System { unwind: true } => 29,
-            RustIntrinsic => 30,
-            RustCall => 31,
-            Unadjusted => 32,
-            RustCold => 33,
-            RiscvInterruptM => 34,
-            RiscvInterruptS => 35,
-        };
-        debug_assert!(
-            AbiDatas
-                .iter()
-                .enumerate()
-                .find(|(_, AbiData { abi, .. })| *abi == self)
-                .map(|(index, _)| index)
-                .expect("abi variant has associated data")
-                == i,
-            "Abi index did not match `AbiDatas` ordering"
-        );
-        i
-    }
-
-    #[inline]
-    pub fn data(self) -> &'static AbiData {
-        &AbiDatas[self.index()]
-    }
-
-    pub fn name(self) -> &'static str {
-        self.data().name
-    }
-}
-
-impl fmt::Display for Abi {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "\"{}\"", self.name())
-    }
-}
diff --git a/compiler/rustc_abi/src/extern_abi/tests.rs b/compiler/rustc_abi/src/extern_abi/tests.rs
index 4823058dd697..fc546a6570f0 100644
--- a/compiler/rustc_abi/src/extern_abi/tests.rs
+++ b/compiler/rustc_abi/src/extern_abi/tests.rs
@@ -1,29 +1,31 @@
 use std::assert_matches::assert_matches;
+use std::str::FromStr;
 
 use super::*;
 
 #[allow(non_snake_case)]
 #[test]
 fn lookup_Rust() {
-    let abi = lookup("Rust");
-    assert!(abi.is_ok() && abi.unwrap().data().name == "Rust");
+    let abi = ExternAbi::from_str("Rust");
+    assert!(abi.is_ok() && abi.unwrap().as_str() == "Rust");
 }
 
 #[test]
 fn lookup_cdecl() {
-    let abi = lookup("cdecl");
-    assert!(abi.is_ok() && abi.unwrap().data().name == "cdecl");
+    let abi = ExternAbi::from_str("cdecl");
+    assert!(abi.is_ok() && abi.unwrap().as_str() == "cdecl");
 }
 
 #[test]
 fn lookup_baz() {
-    let abi = lookup("baz");
-    assert_matches!(abi, Err(AbiUnsupported::Unrecognized));
+    let abi = ExternAbi::from_str("baz");
+    assert_matches!(abi, Err(AbiFromStrErr::Unknown));
 }
 
 #[test]
-fn indices_are_correct() {
-    for (i, abi_data) in AbiDatas.iter().enumerate() {
-        assert_eq!(i, abi_data.abi.index());
-    }
+fn guarantee_lexicographic_ordering() {
+    let abis = ExternAbi::ALL_VARIANTS;
+    let mut sorted_abis = abis.to_vec();
+    sorted_abis.sort_unstable();
+    assert_eq!(abis, sorted_abis);
 }
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index b8773f9ff38f..42250aa173bb 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -2,7 +2,9 @@ use std::fmt::{self, Write};
 use std::ops::{Bound, Deref};
 use std::{cmp, iter};
 
+use rustc_hashes::Hash64;
 use rustc_index::Idx;
+use rustc_index::bit_set::BitMatrix;
 use tracing::debug;
 
 use crate::{
@@ -11,6 +13,9 @@ use crate::{
     Variants, WrappingRange,
 };
 
+mod coroutine;
+mod simple;
+
 #[cfg(feature = "nightly")]
 mod ty;
 
@@ -59,17 +64,28 @@ pub enum LayoutCalculatorError<F> {
 
     /// The fields or variants have irreconcilable reprs
     ReprConflict,
+
+    /// The length of an SIMD type is zero
+    ZeroLengthSimdType,
+
+    /// The length of an SIMD type exceeds the maximum number of lanes
+    OversizedSimdType { max_lanes: u64 },
+
+    /// An element type of an SIMD type isn't a primitive
+    NonPrimitiveSimdType(F),
 }
 
 impl<F> LayoutCalculatorError<F> {
     pub fn without_payload(&self) -> LayoutCalculatorError<()> {
-        match self {
-            LayoutCalculatorError::UnexpectedUnsized(_) => {
-                LayoutCalculatorError::UnexpectedUnsized(())
-            }
-            LayoutCalculatorError::SizeOverflow => LayoutCalculatorError::SizeOverflow,
-            LayoutCalculatorError::EmptyUnion => LayoutCalculatorError::EmptyUnion,
-            LayoutCalculatorError::ReprConflict => LayoutCalculatorError::ReprConflict,
+        use LayoutCalculatorError::*;
+        match *self {
+            UnexpectedUnsized(_) => UnexpectedUnsized(()),
+            SizeOverflow => SizeOverflow,
+            EmptyUnion => EmptyUnion,
+            ReprConflict => ReprConflict,
+            ZeroLengthSimdType => ZeroLengthSimdType,
+            OversizedSimdType { max_lanes } => OversizedSimdType { max_lanes },
+            NonPrimitiveSimdType(_) => NonPrimitiveSimdType(()),
         }
     }
 
@@ -77,13 +93,15 @@ impl<F> LayoutCalculatorError<F> {
     ///
     /// Intended for use by rust-analyzer, as neither it nor `rustc_abi` depend on fluent infra.
     pub fn fallback_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        use LayoutCalculatorError::*;
         f.write_str(match self {
-            LayoutCalculatorError::UnexpectedUnsized(_) => {
-                "an unsized type was found where a sized type was expected"
+            UnexpectedUnsized(_) => "an unsized type was found where a sized type was expected",
+            SizeOverflow => "size overflow",
+            EmptyUnion => "type is a union with no fields",
+            ReprConflict => "type has an invalid repr",
+            ZeroLengthSimdType | OversizedSimdType { .. } | NonPrimitiveSimdType(_) => {
+                "invalid simd type definition"
             }
-            LayoutCalculatorError::SizeOverflow => "size overflow",
-            LayoutCalculatorError::EmptyUnion => "type is a union with no fields",
-            LayoutCalculatorError::ReprConflict => "type has an invalid repr",
         })
     }
 }
@@ -101,40 +119,115 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         Self { cx }
     }
 
-    pub fn scalar_pair<FieldIdx: Idx, VariantIdx: Idx>(
+    pub fn array_like<FieldIdx: Idx, VariantIdx: Idx, F>(
         &self,
-        a: Scalar,
-        b: Scalar,
-    ) -> LayoutData<FieldIdx, VariantIdx> {
-        let dl = self.cx.data_layout();
-        let b_align = b.align(dl);
-        let align = a.align(dl).max(b_align).max(dl.aggregate_align);
-        let b_offset = a.size(dl).align_to(b_align.abi);
-        let size = (b_offset + b.size(dl)).align_to(align.abi);
+        element: &LayoutData<FieldIdx, VariantIdx>,
+        count_if_sized: Option<u64>, // None for slices
+    ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
+        let count = count_if_sized.unwrap_or(0);
+        let size =
+            element.size.checked_mul(count, &self.cx).ok_or(LayoutCalculatorError::SizeOverflow)?;
+
+        Ok(LayoutData {
+            variants: Variants::Single { index: VariantIdx::new(0) },
+            fields: FieldsShape::Array { stride: element.size, count },
+            backend_repr: BackendRepr::Memory { sized: count_if_sized.is_some() },
+            largest_niche: element.largest_niche.filter(|_| count != 0),
+            uninhabited: element.uninhabited && count != 0,
+            align: element.align,
+            size,
+            max_repr_align: None,
+            unadjusted_abi_align: element.align.abi,
+            randomization_seed: element.randomization_seed.wrapping_add(Hash64::new(count)),
+        })
+    }
+
+    pub fn simd_type<
+        FieldIdx: Idx,
+        VariantIdx: Idx,
+        F: AsRef<LayoutData<FieldIdx, VariantIdx>> + fmt::Debug,
+    >(
+        &self,
+        element: F,
+        count: u64,
+        repr_packed: bool,
+    ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
+        let elt = element.as_ref();
+        if count == 0 {
+            return Err(LayoutCalculatorError::ZeroLengthSimdType);
+        } else if count > crate::MAX_SIMD_LANES {
+            return Err(LayoutCalculatorError::OversizedSimdType {
+                max_lanes: crate::MAX_SIMD_LANES,
+            });
+        }
 
-        // HACK(nox): We iter on `b` and then `a` because `max_by_key`
-        // returns the last maximum.
-        let largest_niche = Niche::from_scalar(dl, b_offset, b)
-            .into_iter()
-            .chain(Niche::from_scalar(dl, Size::ZERO, a))
-            .max_by_key(|niche| niche.available(dl));
+        let BackendRepr::Scalar(e_repr) = elt.backend_repr else {
+            return Err(LayoutCalculatorError::NonPrimitiveSimdType(element));
+        };
 
-        let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes());
+        // Compute the size and alignment of the vector
+        let dl = self.cx.data_layout();
+        let size =
+            elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?;
+        let (repr, align) = if repr_packed && !count.is_power_of_two() {
+            // Non-power-of-two vectors have padding up to the next power-of-two.
+            // If we're a packed repr, remove the padding while keeping the alignment as close
+            // to a vector as possible.
+            (
+                BackendRepr::Memory { sized: true },
+                AbiAndPrefAlign {
+                    abi: Align::max_aligned_factor(size),
+                    pref: dl.llvmlike_vector_align(size).pref,
+                },
+            )
+        } else {
+            (BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size))
+        };
+        let size = size.align_to(align.abi);
 
-        LayoutData {
+        Ok(LayoutData {
             variants: Variants::Single { index: VariantIdx::new(0) },
             fields: FieldsShape::Arbitrary {
-                offsets: [Size::ZERO, b_offset].into(),
-                memory_index: [0, 1].into(),
+                offsets: [Size::ZERO].into(),
+                memory_index: [0].into(),
             },
-            backend_repr: BackendRepr::ScalarPair(a, b),
-            largest_niche,
-            align,
+            backend_repr: repr,
+            largest_niche: elt.largest_niche,
+            uninhabited: false,
             size,
+            align,
             max_repr_align: None,
-            unadjusted_abi_align: align.abi,
-            randomization_seed: combined_seed,
-        }
+            unadjusted_abi_align: elt.align.abi,
+            randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
+        })
+    }
+
+    /// Compute the layout for a coroutine.
+    ///
+    /// This uses dedicated code instead of [`Self::layout_of_struct_or_enum`], as coroutine
+    /// fields may be shared between multiple variants (see the [`coroutine`] module for details).
+    pub fn coroutine<
+        'a,
+        F: Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + fmt::Debug + Copy,
+        VariantIdx: Idx,
+        FieldIdx: Idx,
+        LocalIdx: Idx,
+    >(
+        &self,
+        local_layouts: &IndexSlice<LocalIdx, F>,
+        prefix_layouts: IndexVec<FieldIdx, F>,
+        variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
+        storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
+        tag_to_layout: impl Fn(Scalar) -> F,
+    ) -> LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
+        coroutine::layout(
+            self,
+            local_layouts,
+            prefix_layouts,
+            variant_fields,
+            storage_conflicts,
+            tag_to_layout,
+        )
     }
 
     pub fn univariant<
@@ -212,24 +305,6 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         layout
     }
 
-    pub fn layout_of_never_type<FieldIdx: Idx, VariantIdx: Idx>(
-        &self,
-    ) -> LayoutData<FieldIdx, VariantIdx> {
-        let dl = self.cx.data_layout();
-        // This is also used for uninhabited enums, so we use `Variants::Empty`.
-        LayoutData {
-            variants: Variants::Empty,
-            fields: FieldsShape::Primitive,
-            backend_repr: BackendRepr::Uninhabited,
-            largest_niche: None,
-            align: dl.i8_align,
-            size: Size::ZERO,
-            max_repr_align: None,
-            unadjusted_abi_align: dl.i8_align.abi,
-            randomization_seed: 0,
-        }
-    }
-
     pub fn layout_of_struct_or_enum<
         'a,
         FieldIdx: Idx,
@@ -240,7 +315,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         repr: &ReprOptions,
         variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
         is_enum: bool,
-        is_unsafe_cell: bool,
+        is_special_no_niche: bool,
         scalar_valid_range: (Bound<u128>, Bound<u128>),
         discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
         discriminants: impl Iterator<Item = (VariantIdx, i128)>,
@@ -257,7 +332,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
             Some(present_first) => present_first,
             // Uninhabited because it has no variants, or only absent ones.
             None if is_enum => {
-                return Ok(self.layout_of_never_type());
+                return Ok(LayoutData::never_type(&self.cx));
             }
             // If it's a struct, still compute a layout so that we can still compute the
             // field offsets.
@@ -273,7 +348,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                 repr,
                 variants,
                 is_enum,
-                is_unsafe_cell,
+                is_special_no_niche,
                 scalar_valid_range,
                 always_sized,
                 present_first,
@@ -307,10 +382,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
         let mut max_repr_align = repr.align;
 
-        // If all the non-ZST fields have the same ABI and union ABI optimizations aren't
-        // disabled, we can use that common ABI for the union as a whole.
+        // If all the non-ZST fields have the same repr and union repr optimizations aren't
+        // disabled, we can use that common repr for the union as a whole.
         struct AbiMismatch;
-        let mut common_non_zst_abi_and_align = if repr.inhibits_union_abi_opt() {
+        let mut common_non_zst_repr_and_align = if repr.inhibits_union_abi_opt() {
             // Can't optimize
             Err(AbiMismatch)
         } else {
@@ -334,14 +409,14 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                 continue;
             }
 
-            if let Ok(common) = common_non_zst_abi_and_align {
+            if let Ok(common) = common_non_zst_repr_and_align {
                 // Discard valid range information and allow undef
                 let field_abi = field.backend_repr.to_union();
 
                 if let Some((common_abi, common_align)) = common {
                     if common_abi != field_abi {
                         // Different fields have different ABI: disable opt
-                        common_non_zst_abi_and_align = Err(AbiMismatch);
+                        common_non_zst_repr_and_align = Err(AbiMismatch);
                     } else {
                         // Fields with the same non-Aggregate ABI should also
                         // have the same alignment
@@ -354,7 +429,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                     }
                 } else {
                     // First non-ZST field: record its ABI and alignment
-                    common_non_zst_abi_and_align = Ok(Some((field_abi, field.align.abi)));
+                    common_non_zst_repr_and_align = Ok(Some((field_abi, field.align.abi)));
                 }
             }
         }
@@ -373,16 +448,27 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
 
         // If all non-ZST fields have the same ABI, we may forward that ABI
         // for the union as a whole, unless otherwise inhibited.
-        let abi = match common_non_zst_abi_and_align {
+        let backend_repr = match common_non_zst_repr_and_align {
             Err(AbiMismatch) | Ok(None) => BackendRepr::Memory { sized: true },
-            Ok(Some((abi, _))) => {
-                if abi.inherent_align(dl).map(|a| a.abi) != Some(align.abi) {
-                    // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
+            Ok(Some((repr, _))) => match repr {
+                // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
+                BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _)
+                    if repr.scalar_align(dl).unwrap() != align.abi =>
+                {
                     BackendRepr::Memory { sized: true }
-                } else {
-                    abi
                 }
-            }
+                // Vectors require at least element alignment, else disable the opt
+                BackendRepr::SimdVector { element, count: _ }
+                    if element.align(dl).abi > align.abi =>
+                {
+                    BackendRepr::Memory { sized: true }
+                }
+                // the alignment tests passed and we can use this
+                BackendRepr::Scalar(..)
+                | BackendRepr::ScalarPair(..)
+                | BackendRepr::SimdVector { .. }
+                | BackendRepr::Memory { .. } => repr,
+            },
         };
 
         let Some(union_field_count) = NonZeroUsize::new(only_variant.len()) else {
@@ -397,8 +483,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         Ok(LayoutData {
             variants: Variants::Single { index: only_variant_idx },
             fields: FieldsShape::Union(union_field_count),
-            backend_repr: abi,
+            backend_repr,
             largest_niche: None,
+            uninhabited: false,
             align,
             size: size.align_to(align.abi),
             max_repr_align,
@@ -418,7 +505,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         repr: &ReprOptions,
         variants: &IndexSlice<VariantIdx, IndexVec<FieldIdx, F>>,
         is_enum: bool,
-        is_unsafe_cell: bool,
+        is_special_no_niche: bool,
         scalar_valid_range: (Bound<u128>, Bound<u128>),
         always_sized: bool,
         present_first: VariantIdx,
@@ -437,7 +524,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         let mut st = self.univariant(&variants[v], repr, kind)?;
         st.variants = Variants::Single { index: v };
 
-        if is_unsafe_cell {
+        if is_special_no_niche {
             let hide_niches = |scalar: &mut _| match scalar {
                 Scalar::Initialized { value, valid_range } => {
                     *valid_range = WrappingRange::full(value.size(dl))
@@ -446,13 +533,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                 Scalar::Union { .. } => {}
             };
             match &mut st.backend_repr {
-                BackendRepr::Uninhabited => {}
                 BackendRepr::Scalar(scalar) => hide_niches(scalar),
                 BackendRepr::ScalarPair(a, b) => {
                     hide_niches(a);
                     hide_niches(b);
                 }
-                BackendRepr::Vector { element, count: _ } => hide_niches(element),
+                BackendRepr::SimdVector { element, count: _ } => hide_niches(element),
                 BackendRepr::Memory { sized: _ } => {}
             }
             st.largest_niche = None;
@@ -638,9 +724,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
             let same_size = size == variant_layouts[largest_variant_index].size;
             let same_align = align == variant_layouts[largest_variant_index].align;
 
-            let abi = if variant_layouts.iter().all(|v| v.is_uninhabited()) {
-                BackendRepr::Uninhabited
-            } else if same_size && same_align && others_zst {
+            let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited());
+            let abi = if same_size && same_align && others_zst {
                 match variant_layouts[largest_variant_index].backend_repr {
                     // When the total alignment and size match, we can use the
                     // same ABI as the scalar variant with the reserved niche.
@@ -682,6 +767,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                 },
                 backend_repr: abi,
                 largest_niche,
+                uninhabited,
                 size,
                 align,
                 max_repr_align,
@@ -852,9 +938,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         };
         let mut abi = BackendRepr::Memory { sized: true };
 
-        if layout_variants.iter().all(|v| v.is_uninhabited()) {
-            abi = BackendRepr::Uninhabited;
-        } else if tag.size(dl) == size {
+        let uninhabited = layout_variants.iter().all(|v| v.is_uninhabited());
+        if tag.size(dl) == size {
             // Make sure we only use scalar layout when the enum is entirely its
             // own tag (i.e. it has no padding nor any non-ZST variant fields).
             abi = BackendRepr::Scalar(tag);
@@ -936,7 +1021,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                     // Common prim might be uninit.
                     Scalar::Union { value: prim }
                 };
-                let pair = self.scalar_pair::<FieldIdx, VariantIdx>(tag, prim_scalar);
+                let pair =
+                    LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, tag, prim_scalar);
                 let pair_offsets = match pair.fields {
                     FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
                         assert_eq!(memory_index.raw, [0, 1]);
@@ -994,6 +1080,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                 memory_index: [0].into(),
             },
             largest_niche,
+            uninhabited,
             backend_repr: abi,
             align,
             size,
@@ -1058,7 +1145,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
         // unsizable tail fields are excluded so that we use the same seed for the sized and unsized layouts.
         let field_seed = fields_excluding_tail
             .iter()
-            .fold(0u64, |acc, f| acc.wrapping_add(f.randomization_seed));
+            .fold(Hash64::ZERO, |acc, f| acc.wrapping_add(f.randomization_seed));
 
         if optimize_field_order && fields.len() > 1 {
             // If `-Z randomize-layout` was enabled for the type definition we can shuffle
@@ -1072,7 +1159,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                     // `ReprOptions.field_shuffle_seed` is a deterministic seed we can use to randomize field
                     // ordering.
                     let mut rng = rand_xoshiro::Xoshiro128StarStar::seed_from_u64(
-                        field_seed.wrapping_add(repr.field_shuffle_seed),
+                        field_seed.wrapping_add(repr.field_shuffle_seed).as_u64(),
                     );
 
                     // Shuffle the ordering of the fields.
@@ -1302,7 +1389,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                         match field.backend_repr {
                             // For plain scalars, or vectors of them, we can't unpack
                             // newtypes for `#[repr(C)]`, as that affects C ABIs.
-                            BackendRepr::Scalar(_) | BackendRepr::Vector { .. } if optimize_abi => {
+                            BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. }
+                                if optimize_abi =>
+                            {
                                 abi = field.backend_repr;
                             }
                             // But scalar pairs are Rust-specific and get
@@ -1325,7 +1414,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                             } else {
                                 ((j, b), (i, a))
                             };
-                            let pair = self.scalar_pair::<FieldIdx, VariantIdx>(a, b);
+                            let pair =
+                                LayoutData::<FieldIdx, VariantIdx>::scalar_pair(&self.cx, a, b);
                             let pair_offsets = match pair.fields {
                                 FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
                                     assert_eq!(memory_index.raw, [0, 1]);
@@ -1354,9 +1444,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
                 _ => {}
             }
         }
-        if fields.iter().any(|f| f.is_uninhabited()) {
-            abi = BackendRepr::Uninhabited;
-        }
+        let uninhabited = fields.iter().any(|f| f.is_uninhabited());
 
         let unadjusted_abi_align = if repr.transparent() {
             match layout_of_single_non_zst_field {
@@ -1377,6 +1465,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
             fields: FieldsShape::Arbitrary { offsets, memory_index },
             backend_repr: abi,
             largest_niche,
+            uninhabited,
             align,
             size,
             max_repr_align,
diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs
new file mode 100644
index 000000000000..27e704d538c8
--- /dev/null
+++ b/compiler/rustc_abi/src/layout/coroutine.rs
@@ -0,0 +1,320 @@
+//! Coroutine layout logic.
+//!
+//! When laying out coroutines, we divide our saved local fields into two
+//! categories: overlap-eligible and overlap-ineligible.
+//!
+//! Those fields which are ineligible for overlap go in a "prefix" at the
+//! beginning of the layout, and always have space reserved for them.
+//!
+//! Overlap-eligible fields are only assigned to one variant, so we lay
+//! those fields out for each variant and put them right after the
+//! prefix.
+//!
+//! Finally, in the layout details, we point to the fields from the
+//! variants they are assigned to. It is possible for some fields to be
+//! included in multiple variants. No field ever "moves around" in the
+//! layout; its offset is always the same.
+//!
+//! Also included in the layout are the upvars and the discriminant.
+//! These are included as fields on the "outer" layout; they are not part
+//! of any variant.
+
+use std::iter;
+
+use rustc_index::bit_set::{BitMatrix, DenseBitSet};
+use rustc_index::{Idx, IndexSlice, IndexVec};
+use tracing::{debug, trace};
+
+use crate::{
+    BackendRepr, FieldsShape, HasDataLayout, Integer, LayoutData, Primitive, ReprOptions, Scalar,
+    StructKind, TagEncoding, Variants, WrappingRange,
+};
+
+/// Overlap eligibility and variant assignment for each CoroutineSavedLocal.
+#[derive(Clone, Debug, PartialEq)]
+enum SavedLocalEligibility<VariantIdx, FieldIdx> {
+    Unassigned,
+    Assigned(VariantIdx),
+    Ineligible(Option<FieldIdx>),
+}
+
+/// Compute the eligibility and assignment of each local.
+fn coroutine_saved_local_eligibility<VariantIdx: Idx, FieldIdx: Idx, LocalIdx: Idx>(
+    nb_locals: usize,
+    variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
+    storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
+) -> (DenseBitSet<LocalIdx>, IndexVec<LocalIdx, SavedLocalEligibility<VariantIdx, FieldIdx>>) {
+    use SavedLocalEligibility::*;
+
+    let mut assignments: IndexVec<LocalIdx, _> = IndexVec::from_elem_n(Unassigned, nb_locals);
+
+    // The saved locals not eligible for overlap. These will get
+    // "promoted" to the prefix of our coroutine.
+    let mut ineligible_locals = DenseBitSet::new_empty(nb_locals);
+
+    // Figure out which of our saved locals are fields in only
+    // one variant. The rest are deemed ineligible for overlap.
+    for (variant_index, fields) in variant_fields.iter_enumerated() {
+        for local in fields {
+            match assignments[*local] {
+                Unassigned => {
+                    assignments[*local] = Assigned(variant_index);
+                }
+                Assigned(idx) => {
+                    // We've already seen this local at another suspension
+                    // point, so it is no longer a candidate.
+                    trace!(
+                        "removing local {:?} in >1 variant ({:?}, {:?})",
+                        local, variant_index, idx
+                    );
+                    ineligible_locals.insert(*local);
+                    assignments[*local] = Ineligible(None);
+                }
+                Ineligible(_) => {}
+            }
+        }
+    }
+
+    // Next, check every pair of eligible locals to see if they
+    // conflict.
+    for local_a in storage_conflicts.rows() {
+        let conflicts_a = storage_conflicts.count(local_a);
+        if ineligible_locals.contains(local_a) {
+            continue;
+        }
+
+        for local_b in storage_conflicts.iter(local_a) {
+            // local_a and local_b are storage live at the same time, therefore they
+            // cannot overlap in the coroutine layout. The only way to guarantee
+            // this is if they are in the same variant, or one is ineligible
+            // (which means it is stored in every variant).
+            if ineligible_locals.contains(local_b) || assignments[local_a] == assignments[local_b] {
+                continue;
+            }
+
+            // If they conflict, we will choose one to make ineligible.
+            // This is not always optimal; it's just a greedy heuristic that
+            // seems to produce good results most of the time.
+            let conflicts_b = storage_conflicts.count(local_b);
+            let (remove, other) =
+                if conflicts_a > conflicts_b { (local_a, local_b) } else { (local_b, local_a) };
+            ineligible_locals.insert(remove);
+            assignments[remove] = Ineligible(None);
+            trace!("removing local {:?} due to conflict with {:?}", remove, other);
+        }
+    }
+
+    // Count the number of variants in use. If only one of them, then it is
+    // impossible to overlap any locals in our layout. In this case it's
+    // always better to make the remaining locals ineligible, so we can
+    // lay them out with the other locals in the prefix and eliminate
+    // unnecessary padding bytes.
+    {
+        let mut used_variants = DenseBitSet::new_empty(variant_fields.len());
+        for assignment in &assignments {
+            if let Assigned(idx) = assignment {
+                used_variants.insert(*idx);
+            }
+        }
+        if used_variants.count() < 2 {
+            for assignment in assignments.iter_mut() {
+                *assignment = Ineligible(None);
+            }
+            ineligible_locals.insert_all();
+        }
+    }
+
+    // Write down the order of our locals that will be promoted to the prefix.
+    {
+        for (idx, local) in ineligible_locals.iter().enumerate() {
+            assignments[local] = Ineligible(Some(FieldIdx::new(idx)));
+        }
+    }
+    debug!("coroutine saved local assignments: {:?}", assignments);
+
+    (ineligible_locals, assignments)
+}
+
+/// Compute the full coroutine layout.
+pub(super) fn layout<
+    'a,
+    F: core::ops::Deref<Target = &'a LayoutData<FieldIdx, VariantIdx>> + core::fmt::Debug + Copy,
+    VariantIdx: Idx,
+    FieldIdx: Idx,
+    LocalIdx: Idx,
+>(
+    calc: &super::LayoutCalculator<impl HasDataLayout>,
+    local_layouts: &IndexSlice<LocalIdx, F>,
+    mut prefix_layouts: IndexVec<FieldIdx, F>,
+    variant_fields: &IndexSlice<VariantIdx, IndexVec<FieldIdx, LocalIdx>>,
+    storage_conflicts: &BitMatrix<LocalIdx, LocalIdx>,
+    tag_to_layout: impl Fn(Scalar) -> F,
+) -> super::LayoutCalculatorResult<FieldIdx, VariantIdx, F> {
+    use SavedLocalEligibility::*;
+
+    let (ineligible_locals, assignments) =
+        coroutine_saved_local_eligibility(local_layouts.len(), variant_fields, storage_conflicts);
+
+    // Build a prefix layout, including "promoting" all ineligible
+    // locals as part of the prefix. We compute the layout of all of
+    // these fields at once to get optimal packing.
+    let tag_index = prefix_layouts.len();
+
+    // `variant_fields` already accounts for the reserved variants, so no need to add them.
+    let max_discr = (variant_fields.len() - 1) as u128;
+    let discr_int = Integer::fit_unsigned(max_discr);
+    let tag = Scalar::Initialized {
+        value: Primitive::Int(discr_int, /* signed = */ false),
+        valid_range: WrappingRange { start: 0, end: max_discr },
+    };
+
+    let promoted_layouts = ineligible_locals.iter().map(|local| local_layouts[local]);
+    prefix_layouts.push(tag_to_layout(tag));
+    prefix_layouts.extend(promoted_layouts);
+    let prefix =
+        calc.univariant(&prefix_layouts, &ReprOptions::default(), StructKind::AlwaysSized)?;
+
+    let (prefix_size, prefix_align) = (prefix.size, prefix.align);
+
+    // Split the prefix layout into the "outer" fields (upvars and
+    // discriminant) and the "promoted" fields. Promoted fields will
+    // get included in each variant that requested them in
+    // CoroutineLayout.
+    debug!("prefix = {:#?}", prefix);
+    let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields {
+        FieldsShape::Arbitrary { mut offsets, memory_index } => {
+            let mut inverse_memory_index = memory_index.invert_bijective_mapping();
+
+            // "a" (`0..b_start`) and "b" (`b_start..`) correspond to
+            // "outer" and "promoted" fields respectively.
+            let b_start = FieldIdx::new(tag_index + 1);
+            let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index()));
+            let offsets_a = offsets;
+
+            // Disentangle the "a" and "b" components of `inverse_memory_index`
+            // by preserving the order but keeping only one disjoint "half" each.
+            // FIXME(eddyb) build a better abstraction for permutations, if possible.
+            let inverse_memory_index_b: IndexVec<u32, FieldIdx> = inverse_memory_index
+                .iter()
+                .filter_map(|&i| i.index().checked_sub(b_start.index()).map(FieldIdx::new))
+                .collect();
+            inverse_memory_index.raw.retain(|&i| i.index() < b_start.index());
+            let inverse_memory_index_a = inverse_memory_index;
+
+            // Since `inverse_memory_index_{a,b}` each only refer to their
+            // respective fields, they can be safely inverted
+            let memory_index_a = inverse_memory_index_a.invert_bijective_mapping();
+            let memory_index_b = inverse_memory_index_b.invert_bijective_mapping();
+
+            let outer_fields =
+                FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a };
+            (outer_fields, offsets_b, memory_index_b)
+        }
+        _ => unreachable!(),
+    };
+
+    let mut size = prefix.size;
+    let mut align = prefix.align;
+    let variants = variant_fields
+        .iter_enumerated()
+        .map(|(index, variant_fields)| {
+            // Only include overlap-eligible fields when we compute our variant layout.
+            let variant_only_tys = variant_fields
+                .iter()
+                .filter(|local| match assignments[**local] {
+                    Unassigned => unreachable!(),
+                    Assigned(v) if v == index => true,
+                    Assigned(_) => unreachable!("assignment does not match variant"),
+                    Ineligible(_) => false,
+                })
+                .map(|local| local_layouts[*local]);
+
+            let mut variant = calc.univariant(
+                &variant_only_tys.collect::<IndexVec<_, _>>(),
+                &ReprOptions::default(),
+                StructKind::Prefixed(prefix_size, prefix_align.abi),
+            )?;
+            variant.variants = Variants::Single { index };
+
+            let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else {
+                unreachable!();
+            };
+
+            // Now, stitch the promoted and variant-only fields back together in
+            // the order they are mentioned by our CoroutineLayout.
+            // Because we only use some subset (that can differ between variants)
+            // of the promoted fields, we can't just pick those elements of the
+            // `promoted_memory_index` (as we'd end up with gaps).
+            // So instead, we build an "inverse memory_index", as if all of the
+            // promoted fields were being used, but leave the elements not in the
+            // subset as `invalid_field_idx`, which we can filter out later to
+            // obtain a valid (bijective) mapping.
+            let invalid_field_idx = promoted_memory_index.len() + memory_index.len();
+            let mut combined_inverse_memory_index =
+                IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx);
+
+            let mut offsets_and_memory_index = iter::zip(offsets, memory_index);
+            let combined_offsets = variant_fields
+                .iter_enumerated()
+                .map(|(i, local)| {
+                    let (offset, memory_index) = match assignments[*local] {
+                        Unassigned => unreachable!(),
+                        Assigned(_) => {
+                            let (offset, memory_index) = offsets_and_memory_index.next().unwrap();
+                            (offset, promoted_memory_index.len() as u32 + memory_index)
+                        }
+                        Ineligible(field_idx) => {
+                            let field_idx = field_idx.unwrap();
+                            (promoted_offsets[field_idx], promoted_memory_index[field_idx])
+                        }
+                    };
+                    combined_inverse_memory_index[memory_index] = i;
+                    offset
+                })
+                .collect();
+
+            // Remove the unused slots and invert the mapping to obtain the
+            // combined `memory_index` (also see previous comment).
+            combined_inverse_memory_index.raw.retain(|&i| i.index() != invalid_field_idx);
+            let combined_memory_index = combined_inverse_memory_index.invert_bijective_mapping();
+
+            variant.fields = FieldsShape::Arbitrary {
+                offsets: combined_offsets,
+                memory_index: combined_memory_index,
+            };
+
+            size = size.max(variant.size);
+            align = align.max(variant.align);
+            Ok(variant)
+        })
+        .collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
+
+    size = size.align_to(align.abi);
+
+    let uninhabited = prefix.uninhabited || variants.iter().all(|v| v.is_uninhabited());
+    let abi = BackendRepr::Memory { sized: true };
+
+    Ok(LayoutData {
+        variants: Variants::Multiple {
+            tag,
+            tag_encoding: TagEncoding::Direct,
+            tag_field: tag_index,
+            variants,
+        },
+        fields: outer_fields,
+        backend_repr: abi,
+        // Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to
+        // self-referentiality), getting the discriminant can cause aliasing violations.
+        // `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that
+        // would do the same for us here.
+        // See <https://github.com/rust-lang/rust/issues/63818>, <https://github.com/rust-lang/miri/issues/3780>.
+        // FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
+        largest_niche: None,
+        uninhabited,
+        size,
+        align,
+        max_repr_align: None,
+        unadjusted_abi_align: align.abi,
+        randomization_seed: Default::default(),
+    })
+}
diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs
new file mode 100644
index 000000000000..0d0706defc2e
--- /dev/null
+++ b/compiler/rustc_abi/src/layout/simple.rs
@@ -0,0 +1,148 @@
+use std::num::NonZero;
+
+use rustc_hashes::Hash64;
+use rustc_index::{Idx, IndexVec};
+
+use crate::{
+    BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants,
+};
+
+/// "Simple" layout constructors that cannot fail.
+impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
+    pub fn unit<C: HasDataLayout>(cx: &C, sized: bool) -> Self {
+        let dl = cx.data_layout();
+        LayoutData {
+            variants: Variants::Single { index: VariantIdx::new(0) },
+            fields: FieldsShape::Arbitrary {
+                offsets: IndexVec::new(),
+                memory_index: IndexVec::new(),
+            },
+            backend_repr: BackendRepr::Memory { sized },
+            largest_niche: None,
+            uninhabited: false,
+            align: dl.i8_align,
+            size: Size::ZERO,
+            max_repr_align: None,
+            unadjusted_abi_align: dl.i8_align.abi,
+            randomization_seed: Hash64::new(0),
+        }
+    }
+
+    pub fn never_type<C: HasDataLayout>(cx: &C) -> Self {
+        let dl = cx.data_layout();
+        // This is also used for uninhabited enums, so we use `Variants::Empty`.
+        LayoutData {
+            variants: Variants::Empty,
+            fields: FieldsShape::Primitive,
+            backend_repr: BackendRepr::Memory { sized: true },
+            largest_niche: None,
+            uninhabited: true,
+            align: dl.i8_align,
+            size: Size::ZERO,
+            max_repr_align: None,
+            unadjusted_abi_align: dl.i8_align.abi,
+            randomization_seed: Hash64::ZERO,
+        }
+    }
+
+    pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
+        let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
+        let size = scalar.size(cx);
+        let align = scalar.align(cx);
+
+        let range = scalar.valid_range(cx);
+
+        // All primitive types for which we don't have subtype coercions should get a distinct seed,
+        // so that types wrapping them can use randomization to arrive at distinct layouts.
+        //
+        // Some type information is already lost at this point, so as an approximation we derive
+        // the seed from what remains. For example on 64-bit targets usize and u64 can no longer
+        // be distinguished.
+        let randomization_seed = size
+            .bytes()
+            .wrapping_add(
+                match scalar.primitive() {
+                    Primitive::Int(_, true) => 1,
+                    Primitive::Int(_, false) => 2,
+                    Primitive::Float(_) => 3,
+                    Primitive::Pointer(_) => 4,
+                } << 32,
+            )
+            // distinguishes references from pointers
+            .wrapping_add((range.start as u64).rotate_right(16))
+            // distinguishes char from u32 and bool from u8
+            .wrapping_add((range.end as u64).rotate_right(16));
+
+        LayoutData {
+            variants: Variants::Single { index: VariantIdx::new(0) },
+            fields: FieldsShape::Primitive,
+            backend_repr: BackendRepr::Scalar(scalar),
+            largest_niche,
+            uninhabited: false,
+            size,
+            align,
+            max_repr_align: None,
+            unadjusted_abi_align: align.abi,
+            randomization_seed: Hash64::new(randomization_seed),
+        }
+    }
+
+    pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self {
+        let dl = cx.data_layout();
+        let b_align = b.align(dl);
+        let align = a.align(dl).max(b_align).max(dl.aggregate_align);
+        let b_offset = a.size(dl).align_to(b_align.abi);
+        let size = (b_offset + b.size(dl)).align_to(align.abi);
+
+        // HACK(nox): We iter on `b` and then `a` because `max_by_key`
+        // returns the last maximum.
+        let largest_niche = Niche::from_scalar(dl, b_offset, b)
+            .into_iter()
+            .chain(Niche::from_scalar(dl, Size::ZERO, a))
+            .max_by_key(|niche| niche.available(dl));
+
+        let combined_seed = a.size(dl).bytes().wrapping_add(b.size(dl).bytes());
+
+        LayoutData {
+            variants: Variants::Single { index: VariantIdx::new(0) },
+            fields: FieldsShape::Arbitrary {
+                offsets: [Size::ZERO, b_offset].into(),
+                memory_index: [0, 1].into(),
+            },
+            backend_repr: BackendRepr::ScalarPair(a, b),
+            largest_niche,
+            uninhabited: false,
+            align,
+            size,
+            max_repr_align: None,
+            unadjusted_abi_align: align.abi,
+            randomization_seed: Hash64::new(combined_seed),
+        }
+    }
+
+    /// Returns a dummy layout for an uninhabited variant.
+    ///
+    /// Uninhabited variants get pruned as part of the layout calculation,
+    /// so this can be used after the fact to reconstitute a layout.
+    pub fn uninhabited_variant<C: HasDataLayout>(cx: &C, index: VariantIdx, fields: usize) -> Self {
+        let dl = cx.data_layout();
+        LayoutData {
+            variants: Variants::Single { index },
+            fields: match NonZero::new(fields) {
+                Some(fields) => FieldsShape::Union(fields),
+                None => FieldsShape::Arbitrary {
+                    offsets: IndexVec::new(),
+                    memory_index: IndexVec::new(),
+                },
+            },
+            backend_repr: BackendRepr::Memory { sized: true },
+            largest_niche: None,
+            uninhabited: true,
+            align: dl.i8_align,
+            size: Size::ZERO,
+            max_repr_align: None,
+            unadjusted_abi_align: dl.i8_align.abi,
+            randomization_seed: Hash64::ZERO,
+        }
+    }
+}
diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs
index d188750bfe10..4f43c0e6f8e9 100644
--- a/compiler/rustc_abi/src/layout/ty.rs
+++ b/compiler/rustc_abi/src/layout/ty.rs
@@ -1,13 +1,15 @@
 use std::fmt;
 use std::ops::Deref;
 
-use Float::*;
-use Primitive::*;
 use rustc_data_structures::intern::Interned;
 use rustc_macros::HashStable_Generic;
 
+use crate::{
+    AbiAndPrefAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche,
+    PointeeInfo, Primitive, Scalar, Size, TargetDataLayout, Variants,
+};
+
 // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`.
-use crate::{Float, *};
 
 rustc_index::newtype_index! {
     /// The *source-order* index of a field in a variant.
@@ -148,6 +150,12 @@ impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
     }
 }
 
+impl<'a, Ty> AsRef<LayoutData<FieldIdx, VariantIdx>> for TyAndLayout<'a, Ty> {
+    fn as_ref(&self) -> &LayoutData<FieldIdx, VariantIdx> {
+        &*self.layout.0.0
+    }
+}
+
 /// Trait that needs to be implemented by the higher-level type representation
 /// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality.
 pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug {
@@ -197,7 +205,9 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         C: HasDataLayout,
     {
         match self.backend_repr {
-            BackendRepr::Scalar(scalar) => matches!(scalar.primitive(), Float(F32 | F64)),
+            BackendRepr::Scalar(scalar) => {
+                matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64))
+            }
             BackendRepr::Memory { .. } => {
                 if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
                     self.field(cx, 0).is_single_fp_element(cx)
@@ -215,7 +225,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         C: HasDataLayout,
     {
         match self.backend_repr {
-            BackendRepr::Vector { .. } => self.size == expected_size,
+            BackendRepr::SimdVector { .. } => self.size == expected_size,
             BackendRepr::Memory { .. } => {
                 if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
                     self.field(cx, 0).is_single_vector_element(cx, expected_size)
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 8ad33749f347..59b74d292214 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -5,7 +5,6 @@
 #![cfg_attr(feature = "nightly", feature(rustc_attrs))]
 #![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
 #![cfg_attr(feature = "nightly", feature(step_trait))]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 /*! ABI handling for rustc
@@ -50,25 +49,20 @@ use std::str::FromStr;
 use bitflags::bitflags;
 #[cfg(feature = "nightly")]
 use rustc_data_structures::stable_hasher::StableOrd;
+use rustc_hashes::Hash64;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 #[cfg(feature = "nightly")]
-use rustc_macros::HashStable_Generic;
-#[cfg(feature = "nightly")]
-use rustc_macros::{Decodable_Generic, Encodable_Generic};
+use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_Generic};
 
 mod callconv;
 mod layout;
 #[cfg(test)]
 mod tests;
 
-#[cfg(feature = "nightly")]
 mod extern_abi;
 
 pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
-#[cfg(feature = "nightly")]
-pub use extern_abi::{
-    AbiDisabled, AbiUnsupported, ExternAbi, all_names, enabled_names, is_enabled, is_stable, lookup,
-};
+pub use extern_abi::{ExternAbi, all_names};
 #[cfg(feature = "nightly")]
 pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
 pub use layout::{LayoutCalculator, LayoutCalculatorError};
@@ -80,7 +74,10 @@ pub use layout::{LayoutCalculator, LayoutCalculatorError};
 pub trait HashStableContext {}
 
 #[derive(Clone, Copy, PartialEq, Eq, Default)]
-#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic)
+)]
 pub struct ReprFlags(u8);
 
 bitflags! {
@@ -112,7 +109,10 @@ impl std::fmt::Debug for ReprFlags {
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
-#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic)
+)]
 pub enum IntegerType {
     /// Pointer-sized integer type, i.e. `isize` and `usize`. The field shows signedness, e.g.
     /// `Pointer(true)` means `isize`.
@@ -133,7 +133,10 @@ impl IntegerType {
 
 /// Represents the repr options provided by the user.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
-#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic)
+)]
 pub struct ReprOptions {
     pub int: Option<IntegerType>,
     pub align: Option<Align>,
@@ -146,7 +149,7 @@ pub struct ReprOptions {
     /// hash without loss, but it does pay the price of being larger.
     /// Everything's a tradeoff, a 64-bit seed should be sufficient for our
     /// purposes (primarily `-Z randomize-layout`)
-    pub field_shuffle_seed: u64,
+    pub field_shuffle_seed: Hash64,
 }
 
 impl ReprOptions {
@@ -210,6 +213,13 @@ impl ReprOptions {
     }
 }
 
+/// The maximum supported number of lanes in a SIMD vector.
+///
+/// This value is selected based on backend support:
+/// * LLVM does not appear to have a vector width limit.
+/// * Cranelift stores the base-2 log of the lane count in a 4 bit integer.
+pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
+
 /// Parsed [Data layout](https://llvm.org/docs/LangRef.html#data-layout)
 /// for a target, which contains everything needed to compute layouts.
 #[derive(Debug, PartialEq, Eq)]
@@ -334,19 +344,19 @@ impl TargetDataLayout {
                 [p] if p.starts_with('P') => {
                     dl.instruction_address_space = parse_address_space(&p[1..], "P")?
                 }
-                ["a", ref a @ ..] => dl.aggregate_align = parse_align(a, "a")?,
-                ["f16", ref a @ ..] => dl.f16_align = parse_align(a, "f16")?,
-                ["f32", ref a @ ..] => dl.f32_align = parse_align(a, "f32")?,
-                ["f64", ref a @ ..] => dl.f64_align = parse_align(a, "f64")?,
-                ["f128", ref a @ ..] => dl.f128_align = parse_align(a, "f128")?,
+                ["a", a @ ..] => dl.aggregate_align = parse_align(a, "a")?,
+                ["f16", a @ ..] => dl.f16_align = parse_align(a, "f16")?,
+                ["f32", a @ ..] => dl.f32_align = parse_align(a, "f32")?,
+                ["f64", a @ ..] => dl.f64_align = parse_align(a, "f64")?,
+                ["f128", a @ ..] => dl.f128_align = parse_align(a, "f128")?,
                 // FIXME(erikdesjardins): we should be parsing nonzero address spaces
                 // this will require replacing TargetDataLayout::{pointer_size,pointer_align}
                 // with e.g. `fn pointer_size_in(AddressSpace)`
-                [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
+                [p @ "p", s, a @ ..] | [p @ "p0", s, a @ ..] => {
                     dl.pointer_size = parse_size(s, p)?;
                     dl.pointer_align = parse_align(a, p)?;
                 }
-                [s, ref a @ ..] if s.starts_with('i') => {
+                [s, a @ ..] if s.starts_with('i') => {
                     let Ok(bits) = s[1..].parse::<u64>() else {
                         parse_size(&s[1..], "i")?; // For the user error.
                         continue;
@@ -367,7 +377,7 @@ impl TargetDataLayout {
                         dl.i128_align = a;
                     }
                 }
-                [s, ref a @ ..] if s.starts_with('v') => {
+                [s, a @ ..] if s.starts_with('v') => {
                     let v_size = parse_size(&s[1..], "v")?;
                     let a = parse_align(a, s)?;
                     if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) {
@@ -413,16 +423,21 @@ impl TargetDataLayout {
         }
     }
 
+    /// psABI-mandated alignment for a vector type, if any
     #[inline]
-    pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
-        for &(size, align) in &self.vector_align {
-            if size == vec_size {
-                return align;
-            }
-        }
-        // Default to natural alignment, which is what LLVM does.
-        // That is, use the size, rounded up to a power of 2.
-        AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap())
+    fn cabi_vector_align(&self, vec_size: Size) -> Option<AbiAndPrefAlign> {
+        self.vector_align
+            .iter()
+            .find(|(size, _align)| *size == vec_size)
+            .map(|(_size, align)| *align)
+    }
+
+    /// an alignment resembling the one LLVM would pick for a vector
+    #[inline]
+    pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAndPrefAlign {
+        self.cabi_vector_align(vec_size).unwrap_or(AbiAndPrefAlign::new(
+            Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap(),
+        ))
     }
 }
 
@@ -481,7 +496,10 @@ impl FromStr for Endian {
 
 /// Size of a type in bytes.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic)
+)]
 pub struct Size {
     raw: u64,
 }
@@ -707,7 +725,10 @@ impl Step for Size {
 
 /// Alignment of a type in bytes (always a power of two).
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic)
+)]
 pub struct Align {
     pow2: u8,
 }
@@ -796,7 +817,7 @@ impl Align {
     }
 
     #[inline]
-    pub fn bytes(self) -> u64 {
+    pub const fn bytes(self) -> u64 {
         1 << self.pow2
     }
 
@@ -806,7 +827,7 @@ impl Align {
     }
 
     #[inline]
-    pub fn bits(self) -> u64 {
+    pub const fn bits(self) -> u64 {
         self.bytes() * 8
     }
 
@@ -815,20 +836,19 @@ impl Align {
         self.bits().try_into().unwrap()
     }
 
-    /// Computes the best alignment possible for the given offset
-    /// (the largest power of two that the offset is a multiple of).
+    /// Obtain the greatest factor of `size` that is an alignment
+    /// (the largest power of two the Size is a multiple of).
     ///
-    /// N.B., for an offset of `0`, this happens to return `2^64`.
+    /// Note that all numbers are factors of 0
     #[inline]
-    pub fn max_for_offset(offset: Size) -> Align {
-        Align { pow2: offset.bytes().trailing_zeros() as u8 }
+    pub fn max_aligned_factor(size: Size) -> Align {
+        Align { pow2: size.bytes().trailing_zeros() as u8 }
     }
 
-    /// Lower the alignment, if necessary, such that the given offset
-    /// is aligned to it (the offset is a multiple of the alignment).
+    /// Reduces Align to an aligned factor of `size`.
     #[inline]
-    pub fn restrict_for_offset(self, offset: Size) -> Align {
-        self.min(Align::max_for_offset(offset))
+    pub fn restrict_for_offset(self, size: Size) -> Align {
+        self.min(Align::max_aligned_factor(size))
     }
 }
 
@@ -867,7 +887,10 @@ impl AbiAndPrefAlign {
 
 /// Integers, also used for enum discriminants.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
-#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic)
+)]
 pub enum Integer {
     I8,
     I16,
@@ -1184,10 +1207,13 @@ impl Scalar {
     #[inline]
     pub fn is_bool(&self) -> bool {
         use Integer::*;
-        matches!(self, Scalar::Initialized {
-            value: Primitive::Int(I8, false),
-            valid_range: WrappingRange { start: 0, end: 1 }
-        })
+        matches!(
+            self,
+            Scalar::Initialized {
+                value: Primitive::Int(I8, false),
+                valid_range: WrappingRange { start: 0, end: 1 }
+            }
+        )
     }
 
     /// Get the primitive representation of this type, ignoring the valid range and whether the
@@ -1347,7 +1373,7 @@ impl<FieldIdx: Idx> FieldsShape<FieldIdx> {
 
     /// Gets source indices of the fields by increasing offsets.
     #[inline]
-    pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator<Item = usize> + '_ {
+    pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator<Item = usize> {
         let mut inverse_small = [0u8; 64];
         let mut inverse_big = IndexVec::new();
         let use_small = self.count() <= inverse_small.len();
@@ -1406,10 +1432,9 @@ impl AddressSpace {
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
 #[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
 pub enum BackendRepr {
-    Uninhabited,
     Scalar(Scalar),
     ScalarPair(Scalar, Scalar),
-    Vector {
+    SimdVector {
         element: Scalar,
         count: u64,
     },
@@ -1425,10 +1450,9 @@ impl BackendRepr {
     #[inline]
     pub fn is_unsized(&self) -> bool {
         match *self {
-            BackendRepr::Uninhabited
-            | BackendRepr::Scalar(_)
+            BackendRepr::Scalar(_)
             | BackendRepr::ScalarPair(..)
-            | BackendRepr::Vector { .. } => false,
+            | BackendRepr::SimdVector { .. } => false,
             BackendRepr::Memory { sized } => !sized,
         }
     }
@@ -1438,7 +1462,8 @@ impl BackendRepr {
         !self.is_unsized()
     }
 
-    /// Returns `true` if this is a single signed integer scalar
+    /// Returns `true` if this is a single signed integer scalar.
+    /// Sanity check: panics if this is not a scalar type (see PR #70189).
     #[inline]
     pub fn is_signed(&self) -> bool {
         match self {
@@ -1447,12 +1472,6 @@ impl BackendRepr {
         }
     }
 
-    /// Returns `true` if this is an uninhabited type
-    #[inline]
-    pub fn is_uninhabited(&self) -> bool {
-        matches!(*self, BackendRepr::Uninhabited)
-    }
-
     /// Returns `true` if this is a scalar type
     #[inline]
     pub fn is_scalar(&self) -> bool {
@@ -1465,37 +1484,38 @@ impl BackendRepr {
         matches!(*self, BackendRepr::Scalar(s) if s.is_bool())
     }
 
-    /// Returns the fixed alignment of this ABI, if any is mandated.
-    pub fn inherent_align<C: HasDataLayout>(&self, cx: &C) -> Option<AbiAndPrefAlign> {
-        Some(match *self {
-            BackendRepr::Scalar(s) => s.align(cx),
-            BackendRepr::ScalarPair(s1, s2) => s1.align(cx).max(s2.align(cx)),
-            BackendRepr::Vector { element, count } => {
-                cx.data_layout().vector_align(element.size(cx) * count)
-            }
-            BackendRepr::Uninhabited | BackendRepr::Memory { .. } => return None,
-        })
+    /// The psABI alignment for a `Scalar` or `ScalarPair`
+    ///
+    /// `None` for other variants.
+    pub fn scalar_align<C: HasDataLayout>(&self, cx: &C) -> Option<Align> {
+        match *self {
+            BackendRepr::Scalar(s) => Some(s.align(cx).abi),
+            BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi),
+            // The align of a Vector can vary in surprising ways
+            BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None,
+        }
     }
 
-    /// Returns the fixed size of this ABI, if any is mandated.
-    pub fn inherent_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
-        Some(match *self {
-            BackendRepr::Scalar(s) => {
-                // No padding in scalars.
-                s.size(cx)
-            }
+    /// The psABI size for a `Scalar` or `ScalarPair`
+    ///
+    /// `None` for other variants
+    pub fn scalar_size<C: HasDataLayout>(&self, cx: &C) -> Option<Size> {
+        match *self {
+            // No padding in scalars.
+            BackendRepr::Scalar(s) => Some(s.size(cx)),
+            // May have some padding between the pair.
             BackendRepr::ScalarPair(s1, s2) => {
-                // May have some padding between the pair.
                 let field2_offset = s1.size(cx).align_to(s2.align(cx).abi);
-                (field2_offset + s2.size(cx)).align_to(self.inherent_align(cx)?.abi)
-            }
-            BackendRepr::Vector { element, count } => {
-                // No padding in vectors, except possibly for trailing padding
-                // to make the size a multiple of align (e.g. for vectors of size 3).
-                (element.size(cx) * count).align_to(self.inherent_align(cx)?.abi)
+                let size = (field2_offset + s2.size(cx)).align_to(
+                    self.scalar_align(cx)
+                        // We absolutely must have an answer here or everything is FUBAR.
+                        .unwrap(),
+                );
+                Some(size)
             }
-            BackendRepr::Uninhabited | BackendRepr::Memory { .. } => return None,
-        })
+            // The size of a Vector can vary in surprising ways
+            BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None,
+        }
     }
 
     /// Discard validity range information and allow undef.
@@ -1505,12 +1525,10 @@ impl BackendRepr {
             BackendRepr::ScalarPair(s1, s2) => {
                 BackendRepr::ScalarPair(s1.to_union(), s2.to_union())
             }
-            BackendRepr::Vector { element, count } => {
-                BackendRepr::Vector { element: element.to_union(), count }
-            }
-            BackendRepr::Uninhabited | BackendRepr::Memory { .. } => {
-                BackendRepr::Memory { sized: true }
+            BackendRepr::SimdVector { element, count } => {
+                BackendRepr::SimdVector { element: element.to_union(), count }
             }
+            BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true },
         }
     }
 
@@ -1520,8 +1538,8 @@ impl BackendRepr {
             // We do *not* ignore the sign since it matters for some ABIs (e.g. s390x).
             (BackendRepr::Scalar(l), BackendRepr::Scalar(r)) => l.primitive() == r.primitive(),
             (
-                BackendRepr::Vector { element: element_l, count: count_l },
-                BackendRepr::Vector { element: element_r, count: count_r },
+                BackendRepr::SimdVector { element: element_l, count: count_l },
+                BackendRepr::SimdVector { element: element_r, count: count_r },
             ) => element_l.primitive() == element_r.primitive() && count_l == count_r,
             (BackendRepr::ScalarPair(l1, l2), BackendRepr::ScalarPair(r1, r2)) => {
                 l1.primitive() == r1.primitive() && l2.primitive() == r2.primitive()
@@ -1706,6 +1724,11 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
     /// The leaf scalar with the largest number of invalid values
     /// (i.e. outside of its `valid_range`), if it exists.
     pub largest_niche: Option<Niche>,
+    /// Is this type known to be uninhabted?
+    ///
+    /// This is separate from BackendRepr because uninhabited return types can affect ABI,
+    /// especially in the case of by-pointer struct returns, which allocate stack even when unused.
+    pub uninhabited: bool,
 
     pub align: AbiAndPrefAlign,
     pub size: Size,
@@ -1730,62 +1753,21 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
     /// transmuted to `Foo<U>` we aim to create probalistically distinct seeds so that Foo can choose
     /// to reorder its fields based on that information. The current implementation is a conservative
     /// approximation of this goal.
-    pub randomization_seed: u64,
+    pub randomization_seed: Hash64,
 }
 
 impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
     /// Returns `true` if this is an aggregate type (including a ScalarPair!)
     pub fn is_aggregate(&self) -> bool {
         match self.backend_repr {
-            BackendRepr::Uninhabited | BackendRepr::Scalar(_) | BackendRepr::Vector { .. } => false,
+            BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => false,
             BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => true,
         }
     }
 
     /// Returns `true` if this is an uninhabited type
     pub fn is_uninhabited(&self) -> bool {
-        self.backend_repr.is_uninhabited()
-    }
-
-    pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
-        let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
-        let size = scalar.size(cx);
-        let align = scalar.align(cx);
-
-        let range = scalar.valid_range(cx);
-
-        // All primitive types for which we don't have subtype coercions should get a distinct seed,
-        // so that types wrapping them can use randomization to arrive at distinct layouts.
-        //
-        // Some type information is already lost at this point, so as an approximation we derive
-        // the seed from what remains. For example on 64-bit targets usize and u64 can no longer
-        // be distinguished.
-        let randomization_seed = size
-            .bytes()
-            .wrapping_add(
-                match scalar.primitive() {
-                    Primitive::Int(_, true) => 1,
-                    Primitive::Int(_, false) => 2,
-                    Primitive::Float(_) => 3,
-                    Primitive::Pointer(_) => 4,
-                } << 32,
-            )
-            // distinguishes references from pointers
-            .wrapping_add((range.start as u64).rotate_right(16))
-            // distinguishes char from u32 and bool from u8
-            .wrapping_add((range.end as u64).rotate_right(16));
-
-        LayoutData {
-            variants: Variants::Single { index: VariantIdx::new(0) },
-            fields: FieldsShape::Primitive,
-            backend_repr: BackendRepr::Scalar(scalar),
-            largest_niche,
-            size,
-            align,
-            max_repr_align: None,
-            unadjusted_abi_align: align.abi,
-            randomization_seed,
-        }
+        self.uninhabited
     }
 }
 
@@ -1804,17 +1786,19 @@ where
             backend_repr,
             fields,
             largest_niche,
+            uninhabited,
             variants,
             max_repr_align,
             unadjusted_abi_align,
-            ref randomization_seed,
+            randomization_seed,
         } = self;
         f.debug_struct("Layout")
             .field("size", size)
             .field("align", align)
-            .field("abi", backend_repr)
+            .field("backend_repr", backend_repr)
             .field("fields", fields)
             .field("largest_niche", largest_niche)
+            .field("uninhabited", uninhabited)
             .field("variants", variants)
             .field("max_repr_align", max_repr_align)
             .field("unadjusted_abi_align", unadjusted_abi_align)
@@ -1845,7 +1829,7 @@ pub struct PointeeInfo {
     pub safe: Option<PointerKind>,
     /// If `safe` is `Some`, then the pointer is either null or dereferenceable for this many bytes.
     /// On a function argument, "dereferenceable" here means "dereferenceable for the entire duration
-    /// of this function call", i.e. it is UB for the memory that this pointer points to to be freed
+    /// of this function call", i.e. it is UB for the memory that this pointer points to be freed
     /// while this function is still running.
     /// The size can be zero if the pointer is not dereferenceable.
     pub size: Size,
@@ -1876,10 +1860,9 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
     /// non-trivial alignment constraints. You probably want to use `is_1zst` instead.
     pub fn is_zst(&self) -> bool {
         match self.backend_repr {
-            BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) | BackendRepr::Vector { .. } => {
-                false
-            }
-            BackendRepr::Uninhabited => self.size.bytes() == 0,
+            BackendRepr::Scalar(_)
+            | BackendRepr::ScalarPair(..)
+            | BackendRepr::SimdVector { .. } => false,
             BackendRepr::Memory { sized } => sized && self.size.bytes() == 0,
         }
     }
diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml
index 382ab2b07754..bbcd8ea6d389 100644
--- a/compiler/rustc_arena/Cargo.toml
+++ b/compiler/rustc_arena/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustc_arena"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index b21ccba93bb4..6aaac072e4b2 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -23,7 +23,6 @@
 #![feature(maybe_uninit_slice)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::alloc::Layout;
@@ -93,7 +92,7 @@ impl<T> ArenaChunk<T> {
     #[inline]
     fn end(&mut self) -> *mut T {
         unsafe {
-            if mem::size_of::<T>() == 0 {
+            if size_of::<T>() == 0 {
                 // A pointer as large as possible for zero-sized elements.
                 ptr::without_provenance_mut(!0)
             } else {
@@ -151,7 +150,7 @@ impl<T> TypedArena<T> {
         }
 
         unsafe {
-            if mem::size_of::<T>() == 0 {
+            if size_of::<T>() == 0 {
                 self.ptr.set(self.ptr.get().wrapping_byte_add(1));
                 let ptr = ptr::NonNull::<T>::dangling().as_ptr();
                 // Don't drop the object. This `write` is equivalent to `forget`.
@@ -173,13 +172,13 @@ impl<T> TypedArena<T> {
         // FIXME: this should *likely* use `offset_from`, but more
         // investigation is needed (including running tests in miri).
         let available_bytes = self.end.get().addr() - self.ptr.get().addr();
-        let additional_bytes = additional.checked_mul(mem::size_of::<T>()).unwrap();
+        let additional_bytes = additional.checked_mul(size_of::<T>()).unwrap();
         available_bytes >= additional_bytes
     }
 
     #[inline]
     fn alloc_raw_slice(&self, len: usize) -> *mut T {
-        assert!(mem::size_of::<T>() != 0);
+        assert!(size_of::<T>() != 0);
         assert!(len != 0);
 
         // Ensure the current chunk can fit `len` objects.
@@ -213,7 +212,7 @@ impl<T> TypedArena<T> {
         // So we collect all the elements beforehand, which takes care of reentrancy and panic
         // safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
         // doesn't need to be hyper-optimized.
-        assert!(mem::size_of::<T>() != 0);
+        assert!(size_of::<T>() != 0);
 
         let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
         if vec.is_empty() {
@@ -236,7 +235,7 @@ impl<T> TypedArena<T> {
         unsafe {
             // We need the element size to convert chunk sizes (ranging from
             // PAGE to HUGE_PAGE bytes) to element counts.
-            let elem_size = cmp::max(1, mem::size_of::<T>());
+            let elem_size = cmp::max(1, size_of::<T>());
             let mut chunks = self.chunks.borrow_mut();
             let mut new_cap;
             if let Some(last_chunk) = chunks.last_mut() {
@@ -246,7 +245,7 @@ impl<T> TypedArena<T> {
                     // FIXME: this should *likely* use `offset_from`, but more
                     // investigation is needed (including running tests in miri).
                     let used_bytes = self.ptr.get().addr() - last_chunk.start().addr();
-                    last_chunk.entries = used_bytes / mem::size_of::<T>();
+                    last_chunk.entries = used_bytes / size_of::<T>();
                 }
 
                 // If the previous chunk's len is less than HUGE_PAGE
@@ -276,7 +275,7 @@ impl<T> TypedArena<T> {
         let end = self.ptr.get().addr();
         // We then calculate the number of elements to be dropped in the last chunk,
         // which is the filled area's length.
-        let diff = if mem::size_of::<T>() == 0 {
+        let diff = if size_of::<T>() == 0 {
             // `T` is ZST. It can't have a drop flag, so the value here doesn't matter. We get
             // the number of zero-sized values in the last and only chunk, just out of caution.
             // Recall that `end` was incremented for each allocated value.
@@ -284,7 +283,7 @@ impl<T> TypedArena<T> {
         } else {
             // FIXME: this should *likely* use `offset_from`, but more
             // investigation is needed (including running tests in miri).
-            (end - start) / mem::size_of::<T>()
+            (end - start) / size_of::<T>()
         };
         // Pass that to the `destroy` method.
         unsafe {
@@ -329,7 +328,7 @@ fn align_up(val: usize, align: usize) -> usize {
 
 // Pointer alignment is common in compiler types, so keep `DroplessArena` aligned to them
 // to optimize away alignment code.
-const DROPLESS_ALIGNMENT: usize = mem::align_of::<usize>();
+const DROPLESS_ALIGNMENT: usize = align_of::<usize>();
 
 /// An arena that can hold objects of multiple different types that impl `Copy`
 /// and/or satisfy `!mem::needs_drop`.
@@ -447,7 +446,7 @@ impl DroplessArena {
     #[inline]
     pub fn alloc<T>(&self, object: T) -> &mut T {
         assert!(!mem::needs_drop::<T>());
-        assert!(mem::size_of::<T>() != 0);
+        assert!(size_of::<T>() != 0);
 
         let mem = self.alloc_raw(Layout::new::<T>()) as *mut T;
 
@@ -471,7 +470,7 @@ impl DroplessArena {
         T: Copy,
     {
         assert!(!mem::needs_drop::<T>());
-        assert!(mem::size_of::<T>() != 0);
+        assert!(size_of::<T>() != 0);
         assert!(!slice.is_empty());
 
         let mem = self.alloc_raw(Layout::for_value::<[T]>(slice)) as *mut T;
@@ -546,7 +545,7 @@ impl DroplessArena {
         // Warning: this function is reentrant: `iter` could hold a reference to `&self` and
         // allocate additional elements while we're iterating.
         let iter = iter.into_iter();
-        assert!(mem::size_of::<T>() != 0);
+        assert!(size_of::<T>() != 0);
         assert!(!mem::needs_drop::<T>());
 
         let size_hint = iter.size_hint();
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml
index 34c612dac692..b2d3b90fc449 100644
--- a/compiler/rustc_ast/Cargo.toml
+++ b/compiler/rustc_ast/Cargo.toml
@@ -1,16 +1,16 @@
 [package]
 name = "rustc_ast"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
 memchr = "2.7.4"
+rustc-literal-escaper = "0.0.2"
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_index = { path = "../rustc_index" }
-rustc_lexer = { path = "../rustc_lexer" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 8e73df63ef54..1532ca77f713 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -19,6 +19,7 @@
 //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
 
 use std::borrow::Cow;
+use std::sync::Arc;
 use std::{cmp, fmt};
 
 pub use GenericArgs::*;
@@ -27,7 +28,7 @@ pub use rustc_ast_ir::{Movability, Mutability, Pinnedness};
 use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::tagged_ptr::Tag;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 pub use rustc_span::AttrId;
 use rustc_span::source_map::{Spanned, respan};
@@ -38,7 +39,7 @@ pub use crate::format::*;
 use crate::ptr::P;
 use crate::token::{self, CommentKind, Delimiter};
 use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
-use crate::util::parser::{AssocOp, ExprPrecedence};
+use crate::util::parser::{ExprPrecedence, Fixity};
 
 /// A "Label" is an identifier of some point in sources,
 /// e.g. in the following code:
@@ -99,7 +100,7 @@ pub struct Path {
 impl PartialEq<Symbol> for Path {
     #[inline]
     fn eq(&self, symbol: &Symbol) -> bool {
-        self.segments.len() == 1 && { self.segments[0].ident.name == *symbol }
+        matches!(&self.segments[..], [segment] if segment.ident.name == *symbol)
     }
 }
 
@@ -119,14 +120,34 @@ impl Path {
         Path { segments: thin_vec![PathSegment::from_ident(ident)], span: ident.span, tokens: None }
     }
 
+    pub fn is_ident(&self, name: Symbol) -> bool {
+        if let [segment] = self.segments.as_ref()
+            && segment.args.is_none()
+            && segment.ident.name == name
+        {
+            true
+        } else {
+            false
+        }
+    }
+
     pub fn is_global(&self) -> bool {
-        !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot
+        self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
     }
 
-    /// If this path is a single identifier with no arguments, does not ensure
-    /// that the path resolves to a const param, the caller should check this.
-    pub fn is_potential_trivial_const_arg(&self) -> bool {
-        self.segments.len() == 1 && self.segments[0].args.is_none()
+    /// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
+    /// be represented without an anon const in the HIR.
+    ///
+    /// If `allow_mgca_arg` is true (as should be the case in most situations when
+    /// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
+    /// because all paths are valid.
+    ///
+    /// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
+    /// (i.e., it is _potentially_ a const parameter).
+    #[tracing::instrument(level = "debug", ret)]
+    pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
+        allow_mgca_arg
+            || self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
     }
 }
 
@@ -287,6 +308,7 @@ impl ParenthesizedArgs {
     }
 }
 
+use crate::AstDeref;
 pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId};
 
 /// Modifiers on a trait bound like `~const`, `?` and `!`.
@@ -415,9 +437,11 @@ impl WhereClause {
 /// A single predicate in a where-clause.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct WherePredicate {
+    pub attrs: AttrVec,
     pub kind: WherePredicateKind,
     pub id: NodeId,
     pub span: Span,
+    pub is_placeholder: bool,
 }
 
 /// Predicate kind in where-clause.
@@ -532,14 +556,6 @@ pub struct Block {
     pub rules: BlockCheckMode,
     pub span: Span,
     pub tokens: Option<LazyAttrTokenStream>,
-    /// The following *isn't* a parse error, but will cause multiple errors in following stages.
-    /// ```compile_fail
-    /// let x = {
-    ///     foo: var
-    /// };
-    /// ```
-    /// #34255
-    pub could_be_bare_literal: bool,
 }
 
 /// A match pattern.
@@ -558,6 +574,7 @@ impl Pat {
     /// This is intended for use by diagnostics.
     pub fn to_ty(&self) -> Option<P<Ty>> {
         let kind = match &self.kind {
+            PatKind::Missing => unreachable!(),
             // In a type expression `_` is an inference variable.
             PatKind::Wild => TyKind::Infer,
             // An IDENT pattern with no binding mode would be valid as path to a type. E.g. `u32`.
@@ -620,7 +637,8 @@ impl Pat {
             | PatKind::Guard(s, _) => s.walk(it),
 
             // These patterns do not contain subpatterns, skip.
-            PatKind::Wild
+            PatKind::Missing
+            | PatKind::Wild
             | PatKind::Rest
             | PatKind::Never
             | PatKind::Expr(_)
@@ -671,6 +689,7 @@ impl Pat {
     /// Return a name suitable for diagnostics.
     pub fn descr(&self) -> Option<String> {
         match &self.kind {
+            PatKind::Missing => unreachable!(),
             PatKind::Wild => Some("_".to_string()),
             PatKind::Ident(BindingMode::NONE, ident, None) => Some(format!("{ident}")),
             PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())),
@@ -764,6 +783,9 @@ pub enum RangeSyntax {
 // Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum PatKind {
+    /// A missing pattern, e.g. for an anonymous param in a bare fn like `fn f(u32)`.
+    Missing,
+
     /// Represents a wildcard pattern (`_`).
     Wild,
 
@@ -935,8 +957,37 @@ impl BinOpKind {
         matches!(self, BinOpKind::And | BinOpKind::Or)
     }
 
+    pub fn precedence(&self) -> ExprPrecedence {
+        use BinOpKind::*;
+        match *self {
+            Mul | Div | Rem => ExprPrecedence::Product,
+            Add | Sub => ExprPrecedence::Sum,
+            Shl | Shr => ExprPrecedence::Shift,
+            BitAnd => ExprPrecedence::BitAnd,
+            BitXor => ExprPrecedence::BitXor,
+            BitOr => ExprPrecedence::BitOr,
+            Lt | Gt | Le | Ge | Eq | Ne => ExprPrecedence::Compare,
+            And => ExprPrecedence::LAnd,
+            Or => ExprPrecedence::LOr,
+        }
+    }
+
+    pub fn fixity(&self) -> Fixity {
+        use BinOpKind::*;
+        match self {
+            Eq | Ne | Lt | Le | Gt | Ge => Fixity::None,
+            Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => {
+                Fixity::Left
+            }
+        }
+    }
+
     pub fn is_comparison(self) -> bool {
-        crate::util::parser::AssocOp::from_ast_binop(self).is_comparison()
+        use BinOpKind::*;
+        match self {
+            Eq | Ne | Lt | Le | Gt | Ge => true,
+            Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => false,
+        }
     }
 
     /// Returns `true` if the binary operator takes its arguments by value.
@@ -947,6 +998,75 @@ impl BinOpKind {
 
 pub type BinOp = Spanned<BinOpKind>;
 
+// Sometimes `BinOpKind` and `AssignOpKind` need the same treatment. The
+// operations covered by `AssignOpKind` are a subset of those covered by
+// `BinOpKind`, so it makes sense to convert `AssignOpKind` to `BinOpKind`.
+impl From<AssignOpKind> for BinOpKind {
+    fn from(op: AssignOpKind) -> BinOpKind {
+        match op {
+            AssignOpKind::AddAssign => BinOpKind::Add,
+            AssignOpKind::SubAssign => BinOpKind::Sub,
+            AssignOpKind::MulAssign => BinOpKind::Mul,
+            AssignOpKind::DivAssign => BinOpKind::Div,
+            AssignOpKind::RemAssign => BinOpKind::Rem,
+            AssignOpKind::BitXorAssign => BinOpKind::BitXor,
+            AssignOpKind::BitAndAssign => BinOpKind::BitAnd,
+            AssignOpKind::BitOrAssign => BinOpKind::BitOr,
+            AssignOpKind::ShlAssign => BinOpKind::Shl,
+            AssignOpKind::ShrAssign => BinOpKind::Shr,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
+pub enum AssignOpKind {
+    /// The `+=` operator (addition)
+    AddAssign,
+    /// The `-=` operator (subtraction)
+    SubAssign,
+    /// The `*=` operator (multiplication)
+    MulAssign,
+    /// The `/=` operator (division)
+    DivAssign,
+    /// The `%=` operator (modulus)
+    RemAssign,
+    /// The `^=` operator (bitwise xor)
+    BitXorAssign,
+    /// The `&=` operator (bitwise and)
+    BitAndAssign,
+    /// The `|=` operator (bitwise or)
+    BitOrAssign,
+    /// The `<<=` operator (shift left)
+    ShlAssign,
+    /// The `>>=` operator (shift right)
+    ShrAssign,
+}
+
+impl AssignOpKind {
+    pub fn as_str(&self) -> &'static str {
+        use AssignOpKind::*;
+        match self {
+            AddAssign => "+=",
+            SubAssign => "-=",
+            MulAssign => "*=",
+            DivAssign => "/=",
+            RemAssign => "%=",
+            BitXorAssign => "^=",
+            BitAndAssign => "&=",
+            BitOrAssign => "|=",
+            ShlAssign => "<<=",
+            ShrAssign => ">>=",
+        }
+    }
+
+    /// AssignOps are always by value.
+    pub fn is_by_value(self) -> bool {
+        true
+    }
+}
+
+pub type AssignOp = Spanned<AssignOpKind>;
+
 /// Unary operator.
 ///
 /// Note that `&data` is not an operator, it's an `AddrOf` expression.
@@ -1066,6 +1186,7 @@ pub enum MacStmtStyle {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Local {
     pub id: NodeId,
+    pub super_: Option<Span>,
     pub pat: P<Pat>,
     pub ty: Option<P<Ty>>,
     pub kind: LocalKind,
@@ -1175,22 +1296,31 @@ pub struct Expr {
 }
 
 impl Expr {
-    /// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter.
+    /// Check if this expression is potentially a trivial const arg, i.e., one that can _potentially_
+    /// be represented without an anon const in the HIR.
+    ///
+    /// This will unwrap at most one block level (curly braces). After that, if the expression
+    /// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
+    /// See there for more info about `allow_mgca_arg`.
     ///
-    /// If this is not the case, name resolution does not resolve `N` when using
-    /// `min_const_generics` as more complex expressions are not supported.
+    /// The only additional thing to note is that when `allow_mgca_arg` is false, this function
+    /// will only allow paths with no qself, before dispatching to the `Path` function of
+    /// the same name.
     ///
-    /// Does not ensure that the path resolves to a const param, the caller should check this.
+    /// Does not ensure that the path resolves to a const param/item, the caller should check this.
     /// This also does not consider macros, so it's only correct after macro-expansion.
-    pub fn is_potential_trivial_const_arg(&self) -> bool {
+    pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
         let this = self.maybe_unwrap_block();
-
-        if let ExprKind::Path(None, path) = &this.kind
-            && path.is_potential_trivial_const_arg()
-        {
-            true
+        if allow_mgca_arg {
+            matches!(this.kind, ExprKind::Path(..))
         } else {
-            false
+            if let ExprKind::Path(None, path) = &this.kind
+                && path.is_potential_trivial_const_arg(allow_mgca_arg)
+            {
+                true
+            } else {
+                false
+            }
         }
     }
 
@@ -1330,7 +1460,7 @@ impl Expr {
             ExprKind::Range(..) => ExprPrecedence::Range,
 
             // Binop-like expr kinds, handled by `AssocOp`.
-            ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(),
+            ExprKind::Binary(op, ..) => op.node.precedence(),
             ExprKind::Cast(..) => ExprPrecedence::Cast,
 
             ExprKind::Assign(..) |
@@ -1348,6 +1478,7 @@ impl Expr {
             // Never need parens
             ExprKind::Array(_)
             | ExprKind::Await(..)
+            | ExprKind::Use(..)
             | ExprKind::Block(..)
             | ExprKind::Call(..)
             | ExprKind::ConstBlock(_)
@@ -1422,6 +1553,15 @@ pub enum RangeLimits {
     Closed,
 }
 
+impl RangeLimits {
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            RangeLimits::HalfOpen => "..",
+            RangeLimits::Closed => "..=",
+        }
+    }
+}
+
 /// A method call (e.g. `x.foo::<Bar, Baz>(a, b, c)`).
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct MethodCall {
@@ -1528,6 +1668,8 @@ pub enum ExprKind {
     Gen(CaptureBy, P<Block>, GenBlockKind, Span),
     /// An await expression (`my_future.await`). Span is of await keyword.
     Await(P<Expr>, Span),
+    /// A use expression (`x.use`). Span is of use keyword.
+    Use(P<Expr>, Span),
 
     /// A try block (`try { ... }`).
     TryBlock(P<Block>),
@@ -1538,7 +1680,7 @@ pub enum ExprKind {
     /// An assignment with an operator.
     ///
     /// E.g., `a += 1`.
-    AssignOp(BinOp, P<Expr>, P<Expr>),
+    AssignOp(AssignOp, P<Expr>, P<Expr>),
     /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
     Field(P<Expr>, Ident),
     /// An indexing operation (e.g., `foo[2]`).
@@ -1594,7 +1736,7 @@ pub enum ExprKind {
     Try(P<Expr>),
 
     /// A `yield`, with an optional value to be yielded.
-    Yield(Option<P<Expr>>),
+    Yield(YieldKind),
 
     /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
     /// with an optional value to be returned.
@@ -1609,7 +1751,7 @@ pub enum ExprKind {
     /// Added for optimization purposes to avoid the need to escape
     /// large binary blobs - should always behave like [`ExprKind::Lit`]
     /// with a `ByteStr` literal.
-    IncludedBytes(Lrc<[u8]>),
+    IncludedBytes(Arc<[u8]>),
 
     /// A `format_args!()` expression.
     FormatArgs(P<FormatArgs>),
@@ -1655,7 +1797,7 @@ impl GenBlockKind {
 }
 
 /// Whether we're unwrapping or wrapping an unsafe binder
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 #[derive(Encodable, Decodable, HashStable_Generic)]
 pub enum UnsafeBinderCastKind {
     // e.g. `&i32` -> `unsafe<'a> &'a i32`
@@ -1697,8 +1839,17 @@ pub enum CaptureBy {
         /// The span of the `move` keyword.
         move_kw: Span,
     },
-    /// `move` keyword was not specified.
+    /// `move` or `use` keywords were not specified.
     Ref,
+    /// `use |x| y + x`.
+    ///
+    /// Note that if you have a regular closure like `|| x.use`, this will *not* result
+    /// in a `Use` capture. Instead, the `ExprUseVisitor` will look at the type
+    /// of `x` and treat `x.use` as either a copy/clone/move as appropriate.
+    Use {
+        /// The span of the `use` keyword.
+        use_kw: Span,
+    },
 }
 
 /// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
@@ -1831,6 +1982,44 @@ pub enum MatchKind {
     Postfix,
 }
 
+/// The kind of yield expression
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum YieldKind {
+    /// yield expr { ... }
+    Prefix(Option<P<Expr>>),
+    /// expr.yield { ... }
+    Postfix(P<Expr>),
+}
+
+impl YieldKind {
+    /// Returns the expression inside the yield expression, if any.
+    ///
+    /// For postfix yields, this is guaranteed to be `Some`.
+    pub const fn expr(&self) -> Option<&P<Expr>> {
+        match self {
+            YieldKind::Prefix(expr) => expr.as_ref(),
+            YieldKind::Postfix(expr) => Some(expr),
+        }
+    }
+
+    /// Returns a mutable reference to the expression being yielded, if any.
+    pub const fn expr_mut(&mut self) -> Option<&mut P<Expr>> {
+        match self {
+            YieldKind::Prefix(expr) => expr.as_mut(),
+            YieldKind::Postfix(expr) => Some(expr),
+        }
+    }
+
+    /// Returns true if both yields are prefix or both are postfix.
+    pub const fn same_kind(&self, other: &Self) -> bool {
+        match (self, other) {
+            (YieldKind::Prefix(_), YieldKind::Prefix(_)) => true,
+            (YieldKind::Postfix(_), YieldKind::Postfix(_)) => true,
+            _ => false,
+        }
+    }
+}
+
 /// A literal in a meta item.
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct MetaItemLit {
@@ -1902,9 +2091,9 @@ pub enum LitKind {
     Str(Symbol, StrStyle),
     /// A byte string (`b"foo"`). Not stored as a symbol because it might be
     /// non-utf8, and symbols only allow utf8 strings.
-    ByteStr(Lrc<[u8]>, StrStyle),
+    ByteStr(Arc<[u8]>, StrStyle),
     /// A C String (`c"foo"`). Guaranteed to only have `\0` at the end.
-    CStr(Lrc<[u8]>, StrStyle),
+    CStr(Arc<[u8]>, StrStyle),
     /// A byte char (`b'f'`).
     Byte(u8),
     /// A character literal (`'a'`).
@@ -2165,6 +2354,14 @@ impl Ty {
         }
         final_ty
     }
+
+    pub fn is_maybe_parenthesised_infer(&self) -> bool {
+        match &self.kind {
+            TyKind::Infer => true,
+            TyKind::Paren(inner) => inner.ast_deref().is_maybe_parenthesised_infer(),
+            _ => false,
+        }
+    }
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
@@ -2239,7 +2436,7 @@ pub enum TyKind {
     CVarArgs,
     /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero<u32>`,
     /// just as part of the type system.
-    Pat(P<Ty>, P<Pat>),
+    Pat(P<Ty>, P<TyPat>),
     /// Sometimes we need a dummy value when no error has occurred.
     Dummy,
     /// Placeholder for a kind that has failed to be defined.
@@ -2267,12 +2464,55 @@ impl TyKind {
     }
 }
 
+/// A pattern type pattern.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct TyPat {
+    pub id: NodeId,
+    pub kind: TyPatKind,
+    pub span: Span,
+    pub tokens: Option<LazyAttrTokenStream>,
+}
+
+/// All the different flavors of pattern that Rust recognizes.
+//
+// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum TyPatKind {
+    /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
+    Range(Option<P<AnonConst>>, Option<P<AnonConst>>, Spanned<RangeEnd>),
+
+    /// Placeholder for a pattern that wasn't syntactically well formed in some way.
+    Err(ErrorGuaranteed),
+}
+
 /// Syntax used to declare a trait object.
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[repr(u8)]
 pub enum TraitObjectSyntax {
-    Dyn,
-    DynStar,
-    None,
+    // SAFETY: When adding new variants make sure to update the `Tag` impl.
+    Dyn = 0,
+    DynStar = 1,
+    None = 2,
+}
+
+/// SAFETY: `TraitObjectSyntax` only has 3 data-less variants which means
+/// it can be represented with a `u2`. We use `repr(u8)` to guarantee the
+/// discriminants of the variants are no greater than `3`.
+unsafe impl Tag for TraitObjectSyntax {
+    const BITS: u32 = 2;
+
+    fn into_usize(self) -> usize {
+        self as u8 as usize
+    }
+
+    unsafe fn from_usize(tag: usize) -> Self {
+        match tag {
+            0 => TraitObjectSyntax::Dyn,
+            1 => TraitObjectSyntax::DynStar,
+            2 => TraitObjectSyntax::None,
+            _ => unreachable!(),
+        }
+    }
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
@@ -2530,6 +2770,8 @@ pub enum SelfKind {
     Value(Mutability),
     /// `&'lt self`, `&'lt mut self`
     Region(Option<Lifetime>, Mutability),
+    /// `&'lt pin const self`, `&'lt pin mut self`
+    Pinned(Option<Lifetime>, Mutability),
     /// `self: TYPE`, `mut self: TYPE`
     Explicit(P<Ty>, Mutability),
 }
@@ -2539,6 +2781,8 @@ impl SelfKind {
         match self {
             SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
             SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
+            SelfKind::Pinned(None, mutbl) => format!("&pin {}", mutbl.ptr_str()),
+            SelfKind::Pinned(Some(lt), mutbl) => format!("&{lt} pin {}", mutbl.ptr_str()),
             SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
                 unreachable!("if we had an explicit self, we wouldn't be here")
             }
@@ -2555,11 +2799,13 @@ impl Param {
             if ident.name == kw::SelfLower {
                 return match self.ty.kind {
                     TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
-                    TyKind::Ref(lt, MutTy { ref ty, mutbl })
-                    | TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
+                    TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
+                        Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
+                    }
+                    TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
                         if ty.kind.is_implicit_self() =>
                     {
-                        Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
+                        Some(respan(self.pat.span, SelfKind::Pinned(lt, mutbl)))
                     }
                     _ => Some(respan(
                         self.pat.span.to(self.ty.span),
@@ -2601,6 +2847,15 @@ impl Param {
                     tokens: None,
                 }),
             ),
+            SelfKind::Pinned(lt, mutbl) => (
+                mutbl,
+                P(Ty {
+                    id: DUMMY_NODE_ID,
+                    kind: TyKind::PinnedRef(lt, MutTy { ty: infer_ty, mutbl }),
+                    span,
+                    tokens: None,
+                }),
+            ),
         };
         Param {
             attrs,
@@ -3135,9 +3390,6 @@ pub struct Item<K = ItemKind> {
     pub id: NodeId,
     pub span: Span,
     pub vis: Visibility,
-    /// The name of the item.
-    /// It might be a dummy name in case of anonymous items.
-    pub ident: Ident,
 
     pub kind: K,
 
@@ -3159,23 +3411,23 @@ impl Item {
 
     pub fn opt_generics(&self) -> Option<&Generics> {
         match &self.kind {
-            ItemKind::ExternCrate(_)
+            ItemKind::ExternCrate(..)
             | ItemKind::Use(_)
-            | ItemKind::Mod(_, _)
+            | ItemKind::Mod(..)
             | ItemKind::ForeignMod(_)
             | ItemKind::GlobalAsm(_)
             | ItemKind::MacCall(_)
             | ItemKind::Delegation(_)
             | ItemKind::DelegationMac(_)
-            | ItemKind::MacroDef(_) => None,
+            | ItemKind::MacroDef(..) => None,
             ItemKind::Static(_) => None,
             ItemKind::Const(i) => Some(&i.generics),
             ItemKind::Fn(i) => Some(&i.generics),
             ItemKind::TyAlias(i) => Some(&i.generics),
-            ItemKind::TraitAlias(generics, _)
-            | ItemKind::Enum(_, generics)
-            | ItemKind::Struct(_, generics)
-            | ItemKind::Union(_, generics) => Some(&generics),
+            ItemKind::TraitAlias(_, generics, _)
+            | ItemKind::Enum(_, _, generics)
+            | ItemKind::Struct(_, _, generics)
+            | ItemKind::Union(_, _, generics) => Some(&generics),
             ItemKind::Trait(i) => Some(&i.generics),
             ItemKind::Impl(i) => Some(&i.generics),
         }
@@ -3193,7 +3445,7 @@ pub enum Extern {
     ///
     /// E.g. `extern fn foo() {}`.
     ///
-    /// This is just `extern "C"` (see `rustc_target::spec::abi::Abi::FALLBACK`).
+    /// This is just `extern "C"` (see `rustc_abi::ExternAbi::FALLBACK`).
     Implicit(Span),
     /// An explicit extern keyword was used with an explicit ABI.
     ///
@@ -3252,6 +3504,7 @@ impl Default for FnHeader {
 pub struct Trait {
     pub safety: Safety,
     pub is_auto: IsAuto,
+    pub ident: Ident,
     pub generics: Generics,
     pub bounds: GenericBounds,
     pub items: ThinVec<P<AssocItem>>,
@@ -3297,6 +3550,7 @@ pub struct TyAliasWhereClauses {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct TyAlias {
     pub defaultness: Defaultness,
+    pub ident: Ident,
     pub generics: Generics,
     pub where_clauses: TyAliasWhereClauses,
     pub bounds: GenericBounds,
@@ -3316,11 +3570,20 @@ pub struct Impl {
     pub items: ThinVec<P<AssocItem>>,
 }
 
+#[derive(Clone, Encodable, Decodable, Debug, Default)]
+pub struct FnContract {
+    pub requires: Option<P<Expr>>,
+    pub ensures: Option<P<Expr>>,
+}
+
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Fn {
     pub defaultness: Defaultness,
+    pub ident: Ident,
     pub generics: Generics,
     pub sig: FnSig,
+    pub contract: Option<P<FnContract>>,
+    pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
     pub body: Option<P<Block>>,
 }
 
@@ -3330,6 +3593,7 @@ pub struct Delegation {
     pub id: NodeId,
     pub qself: Option<P<QSelf>>,
     pub path: Path,
+    pub ident: Ident,
     pub rename: Option<Ident>,
     pub body: Option<P<Block>>,
     /// The item was expanded from a glob delegation item.
@@ -3347,18 +3611,22 @@ pub struct DelegationMac {
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct StaticItem {
+    pub ident: Ident,
     pub ty: P<Ty>,
     pub safety: Safety,
     pub mutability: Mutability,
     pub expr: Option<P<Expr>>,
+    pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct ConstItem {
     pub defaultness: Defaultness,
+    pub ident: Ident,
     pub generics: Generics,
     pub ty: P<Ty>,
     pub expr: Option<P<Expr>>,
+    pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
 }
 
 // Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
@@ -3367,7 +3635,7 @@ pub enum ItemKind {
     /// An `extern crate` item, with the optional *original* crate name if the crate was renamed.
     ///
     /// E.g., `extern crate foo` or `extern crate foo_bar as foo`.
-    ExternCrate(Option<Symbol>),
+    ExternCrate(Option<Symbol>, Ident),
     /// A use declaration item (`use`).
     ///
     /// E.g., `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`.
@@ -3389,7 +3657,7 @@ pub enum ItemKind {
     /// E.g., `mod foo;` or `mod foo { .. }`.
     /// `unsafe` keyword on modules is accepted syntactically for macro DSLs, but not
     /// semantically by Rust.
-    Mod(Safety, ModKind),
+    Mod(Safety, Ident, ModKind),
     /// An external module (`extern`).
     ///
     /// E.g., `extern {}` or `extern "C" {}`.
@@ -3403,15 +3671,15 @@ pub enum ItemKind {
     /// An enum definition (`enum`).
     ///
     /// E.g., `enum Foo<A, B> { C<A>, D<B> }`.
-    Enum(EnumDef, Generics),
+    Enum(Ident, EnumDef, Generics),
     /// A struct definition (`struct`).
     ///
     /// E.g., `struct Foo<A> { x: A }`.
-    Struct(VariantData, Generics),
+    Struct(Ident, VariantData, Generics),
     /// A union definition (`union`).
     ///
     /// E.g., `union Foo<A, B> { x: A, y: B }`.
-    Union(VariantData, Generics),
+    Union(Ident, VariantData, Generics),
     /// A trait declaration (`trait`).
     ///
     /// E.g., `trait Foo { .. }`, `trait Foo<T> { .. }` or `auto trait Foo {}`.
@@ -3419,7 +3687,7 @@ pub enum ItemKind {
     /// Trait alias.
     ///
     /// E.g., `trait Foo = Bar + Quux;`.
-    TraitAlias(Generics, GenericBounds),
+    TraitAlias(Ident, Generics, GenericBounds),
     /// An implementation.
     ///
     /// E.g., `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`.
@@ -3430,7 +3698,7 @@ pub enum ItemKind {
     MacCall(P<MacCall>),
 
     /// A macro definition.
-    MacroDef(MacroDef),
+    MacroDef(Ident, MacroDef),
 
     /// A single delegation item (`reuse`).
     ///
@@ -3442,6 +3710,31 @@ pub enum ItemKind {
 }
 
 impl ItemKind {
+    pub fn ident(&self) -> Option<Ident> {
+        match *self {
+            ItemKind::ExternCrate(_, ident)
+            | ItemKind::Static(box StaticItem { ident, .. })
+            | ItemKind::Const(box ConstItem { ident, .. })
+            | ItemKind::Fn(box Fn { ident, .. })
+            | ItemKind::Mod(_, ident, _)
+            | ItemKind::TyAlias(box TyAlias { ident, .. })
+            | ItemKind::Enum(ident, ..)
+            | ItemKind::Struct(ident, ..)
+            | ItemKind::Union(ident, ..)
+            | ItemKind::Trait(box Trait { ident, .. })
+            | ItemKind::TraitAlias(ident, ..)
+            | ItemKind::MacroDef(ident, _)
+            | ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
+
+            ItemKind::Use(_)
+            | ItemKind::ForeignMod(_)
+            | ItemKind::GlobalAsm(_)
+            | ItemKind::Impl(_)
+            | ItemKind::MacCall(_)
+            | ItemKind::DelegationMac(_) => None,
+        }
+    }
+
     /// "a" or "an"
     pub fn article(&self) -> &'static str {
         use ItemKind::*;
@@ -3482,11 +3775,11 @@ impl ItemKind {
             Self::Fn(box Fn { generics, .. })
             | Self::TyAlias(box TyAlias { generics, .. })
             | Self::Const(box ConstItem { generics, .. })
-            | Self::Enum(_, generics)
-            | Self::Struct(_, generics)
-            | Self::Union(_, generics)
+            | Self::Enum(_, _, generics)
+            | Self::Struct(_, _, generics)
+            | Self::Union(_, _, generics)
             | Self::Trait(box Trait { generics, .. })
-            | Self::TraitAlias(generics, _)
+            | Self::TraitAlias(_, generics, _)
             | Self::Impl(box Impl { generics, .. }) => Some(generics),
             _ => None,
         }
@@ -3522,6 +3815,17 @@ pub enum AssocItemKind {
 }
 
 impl AssocItemKind {
+    pub fn ident(&self) -> Option<Ident> {
+        match *self {
+            AssocItemKind::Const(box ConstItem { ident, .. })
+            | AssocItemKind::Fn(box Fn { ident, .. })
+            | AssocItemKind::Type(box TyAlias { ident, .. })
+            | AssocItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
+
+            AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(_) => None,
+        }
+    }
+
     pub fn defaultness(&self) -> Defaultness {
         match *self {
             Self::Const(box ConstItem { defaultness, .. })
@@ -3568,14 +3872,26 @@ impl TryFrom<ItemKind> for AssocItemKind {
 pub enum ForeignItemKind {
     /// A foreign static item (`static FOO: u8`).
     Static(Box<StaticItem>),
-    /// An foreign function.
+    /// A foreign function.
     Fn(Box<Fn>),
-    /// An foreign type.
+    /// A foreign type.
     TyAlias(Box<TyAlias>),
     /// A macro expanding to foreign items.
     MacCall(P<MacCall>),
 }
 
+impl ForeignItemKind {
+    pub fn ident(&self) -> Option<Ident> {
+        match *self {
+            ForeignItemKind::Static(box StaticItem { ident, .. })
+            | ForeignItemKind::Fn(box Fn { ident, .. })
+            | ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => Some(ident),
+
+            ForeignItemKind::MacCall(_) => None,
+        }
+    }
+}
+
 impl From<ForeignItemKind> for ItemKind {
     fn from(foreign_item_kind: ForeignItemKind) -> ItemKind {
         match foreign_item_kind {
@@ -3612,23 +3928,23 @@ mod size_asserts {
 
     use super::*;
     // tidy-alphabetical-start
-    static_assert_size!(AssocItem, 88);
+    static_assert_size!(AssocItem, 80);
     static_assert_size!(AssocItemKind, 16);
     static_assert_size!(Attribute, 32);
     static_assert_size!(Block, 32);
     static_assert_size!(Expr, 72);
     static_assert_size!(ExprKind, 40);
-    static_assert_size!(Fn, 160);
-    static_assert_size!(ForeignItem, 88);
+    static_assert_size!(Fn, 184);
+    static_assert_size!(ForeignItem, 80);
     static_assert_size!(ForeignItemKind, 16);
     static_assert_size!(GenericArg, 24);
     static_assert_size!(GenericBound, 88);
     static_assert_size!(Generics, 40);
     static_assert_size!(Impl, 136);
-    static_assert_size!(Item, 136);
-    static_assert_size!(ItemKind, 64);
+    static_assert_size!(Item, 144);
+    static_assert_size!(ItemKind, 80);
     static_assert_size!(LitKind, 24);
-    static_assert_size!(Local, 80);
+    static_assert_size!(Local, 96);
     static_assert_size!(MetaItemLit, 40);
     static_assert_size!(Param, 40);
     static_assert_size!(Pat, 72);
diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs
index 60f8c6e10481..7f98e7ba8a61 100644
--- a/compiler/rustc_ast/src/ast_traits.rs
+++ b/compiler/rustc_ast/src/ast_traits.rs
@@ -6,12 +6,11 @@ use std::fmt;
 use std::marker::PhantomData;
 
 use crate::ptr::P;
-use crate::token::Nonterminal;
 use crate::tokenstream::LazyAttrTokenStream;
 use crate::{
     Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField,
     FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind,
-    Ty, Variant, Visibility,
+    Ty, Variant, Visibility, WherePredicate,
 };
 
 /// A utility trait to reduce boilerplate.
@@ -79,6 +78,7 @@ impl_has_node_id!(
     Stmt,
     Ty,
     Variant,
+    WherePredicate,
 );
 
 impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T {
@@ -127,7 +127,16 @@ macro_rules! impl_has_tokens_none {
 }
 
 impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
-impl_has_tokens_none!(Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant);
+impl_has_tokens_none!(
+    Arm,
+    ExprField,
+    FieldDef,
+    GenericParam,
+    Param,
+    PatField,
+    Variant,
+    WherePredicate
+);
 
 impl<T: AstDeref<Target: HasTokens>> HasTokens for T {
     fn tokens(&self) -> Option<&LazyAttrTokenStream> {
@@ -196,35 +205,6 @@ impl HasTokens for Attribute {
     }
 }
 
-impl HasTokens for Nonterminal {
-    fn tokens(&self) -> Option<&LazyAttrTokenStream> {
-        match self {
-            Nonterminal::NtItem(item) => item.tokens(),
-            Nonterminal::NtStmt(stmt) => stmt.tokens(),
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens(),
-            Nonterminal::NtPat(pat) => pat.tokens(),
-            Nonterminal::NtTy(ty) => ty.tokens(),
-            Nonterminal::NtMeta(attr_item) => attr_item.tokens(),
-            Nonterminal::NtPath(path) => path.tokens(),
-            Nonterminal::NtVis(vis) => vis.tokens(),
-            Nonterminal::NtBlock(block) => block.tokens(),
-        }
-    }
-    fn tokens_mut(&mut self) -> Option<&mut Option<LazyAttrTokenStream>> {
-        match self {
-            Nonterminal::NtItem(item) => item.tokens_mut(),
-            Nonterminal::NtStmt(stmt) => stmt.tokens_mut(),
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(),
-            Nonterminal::NtPat(pat) => pat.tokens_mut(),
-            Nonterminal::NtTy(ty) => ty.tokens_mut(),
-            Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(),
-            Nonterminal::NtPath(path) => path.tokens_mut(),
-            Nonterminal::NtVis(vis) => vis.tokens_mut(),
-            Nonterminal::NtBlock(block) => block.tokens_mut(),
-        }
-    }
-}
-
 /// A trait for AST nodes having (or not having) attributes.
 pub trait HasAttrs {
     /// This is `true` if this `HasAttrs` might support 'custom' (proc-macro) inner
@@ -289,6 +269,7 @@ impl_has_attrs!(
     Param,
     PatField,
     Variant,
+    WherePredicate,
 );
 impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
 
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 51f185800132..d656d9b0b8af 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -14,7 +14,7 @@ use crate::ast::{
     PathSegment, Safety,
 };
 use crate::ptr::P;
-use crate::token::{self, CommentKind, Delimiter, Token};
+use crate::token::{self, CommentKind, Delimiter, InvisibleOrigin, MetaVarKind, Token};
 use crate::tokenstream::{
     DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
 };
@@ -302,7 +302,7 @@ impl AttrItem {
 impl MetaItem {
     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
     pub fn ident(&self) -> Option<Ident> {
-        if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
+        if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None }
     }
 
     pub fn name_or_empty(&self) -> Symbol {
@@ -405,11 +405,17 @@ impl MetaItem {
                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
                 Path { span, segments, tokens: None }
             }
-            Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt {
-                token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
-                token::Nonterminal::NtPath(path) => (**path).clone(),
-                _ => return None,
-            },
+            Some(TokenTree::Delimited(
+                _span,
+                _spacing,
+                Delimiter::Invisible(InvisibleOrigin::MetaVar(
+                    MetaVarKind::Meta { .. } | MetaVarKind::Path,
+                )),
+                _stream,
+            )) => {
+                // This path is currently unreachable in the test suite.
+                unreachable!()
+            }
             Some(TokenTree::Token(
                 Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. },
                 _,
@@ -564,6 +570,14 @@ impl MetaItemInner {
         }
     }
 
+    /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.
+    pub fn boolean_literal(&self) -> Option<bool> {
+        match self {
+            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),
+            _ => None,
+        }
+    }
+
     /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's
     /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.
     pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {
@@ -613,7 +627,7 @@ pub fn mk_doc_comment(
     Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
 }
 
-pub fn mk_attr(
+fn mk_attr(
     g: &AttrIdGenerator,
     style: AttrStyle,
     unsafety: Safety,
diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs
index 7ef8bc179738..13a7c5a18057 100644
--- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs
+++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs
@@ -17,7 +17,6 @@ use crate::{Ty, TyKind};
 /// functions. The proper solution is to recognize and resolve this DAG of autodiff invocations,
 /// as it's already done in the C++ and Julia frontend of Enzyme.
 ///
-/// (FIXME) remove *First variants.
 /// Documentation for using [reverse](https://enzyme.mit.edu/rust/rev.html) and
 /// [forward](https://enzyme.mit.edu/rust/fwd.html) mode is available online.
 #[derive(Clone, Copy, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
@@ -30,14 +29,6 @@ pub enum DiffMode {
     Forward,
     /// The target function, to be created using reverse mode AD.
     Reverse,
-    /// The target function, to be created using forward mode AD.
-    /// This target function will also be used as a source for higher order derivatives,
-    /// so compute it before all Forward/Reverse targets and optimize it through llvm.
-    ForwardFirst,
-    /// The target function, to be created using reverse mode AD.
-    /// This target function will also be used as a source for higher order derivatives,
-    /// so compute it before all Forward/Reverse targets and optimize it through llvm.
-    ReverseFirst,
 }
 
 /// Dual and Duplicated (and their Only variants) are getting lowered to the same Enzyme Activity.
@@ -79,22 +70,40 @@ pub struct AutoDiffItem {
     pub target: String,
     pub attrs: AutoDiffAttrs,
 }
+
 #[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct AutoDiffAttrs {
     /// Conceptually either forward or reverse mode AD, as described in various autodiff papers and
     /// e.g. in the [JAX
     /// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions).
     pub mode: DiffMode,
+    /// A user-provided, batching width. If not given, we will default to 1 (no batching).
+    /// Calling a differentiated, non-batched function through a loop 100 times is equivalent to:
+    /// - Calling the function 50 times with a batch size of 2
+    /// - Calling the function 25 times with a batch size of 4,
+    /// etc. A batched function takes more (or longer) arguments, and might be able to benefit from
+    /// cache locality, better re-usal of primal values, and other optimizations.
+    /// We will (before LLVM's vectorizer runs) just generate most LLVM-IR instructions `width`
+    /// times, so this massively increases code size. As such, values like 1024 are unlikely to
+    /// work. We should consider limiting this to u8 or u16, but will leave it at u32 for
+    /// experiments for now and focus on documenting the implications of a large width.
+    pub width: u32,
     pub ret_activity: DiffActivity,
     pub input_activity: Vec<DiffActivity>,
 }
 
+impl AutoDiffAttrs {
+    pub fn has_primal_ret(&self) -> bool {
+        matches!(self.ret_activity, DiffActivity::Active | DiffActivity::Dual)
+    }
+}
+
 impl DiffMode {
     pub fn is_rev(&self) -> bool {
-        matches!(self, DiffMode::Reverse | DiffMode::ReverseFirst)
+        matches!(self, DiffMode::Reverse)
     }
     pub fn is_fwd(&self) -> bool {
-        matches!(self, DiffMode::Forward | DiffMode::ForwardFirst)
+        matches!(self, DiffMode::Forward)
     }
 }
 
@@ -105,8 +114,6 @@ impl Display for DiffMode {
             DiffMode::Source => write!(f, "Source"),
             DiffMode::Forward => write!(f, "Forward"),
             DiffMode::Reverse => write!(f, "Reverse"),
-            DiffMode::ForwardFirst => write!(f, "ForwardFirst"),
-            DiffMode::ReverseFirst => write!(f, "ReverseFirst"),
         }
     }
 }
@@ -124,12 +131,12 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
     match mode {
         DiffMode::Error => false,
         DiffMode::Source => false,
-        DiffMode::Forward | DiffMode::ForwardFirst => {
+        DiffMode::Forward => {
             activity == DiffActivity::Dual
                 || activity == DiffActivity::DualOnly
                 || activity == DiffActivity::Const
         }
-        DiffMode::Reverse | DiffMode::ReverseFirst => {
+        DiffMode::Reverse => {
             activity == DiffActivity::Const
                 || activity == DiffActivity::Active
                 || activity == DiffActivity::ActiveOnly
@@ -165,10 +172,10 @@ pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool {
     return match mode {
         DiffMode::Error => false,
         DiffMode::Source => false,
-        DiffMode::Forward | DiffMode::ForwardFirst => {
+        DiffMode::Forward => {
             matches!(activity, Dual | DualOnly | Const)
         }
-        DiffMode::Reverse | DiffMode::ReverseFirst => {
+        DiffMode::Reverse => {
             matches!(activity, Active | ActiveOnly | Duplicated | DuplicatedOnly | Const)
         }
     };
@@ -199,8 +206,6 @@ impl FromStr for DiffMode {
             "Source" => Ok(DiffMode::Source),
             "Forward" => Ok(DiffMode::Forward),
             "Reverse" => Ok(DiffMode::Reverse),
-            "ForwardFirst" => Ok(DiffMode::ForwardFirst),
-            "ReverseFirst" => Ok(DiffMode::ReverseFirst),
             _ => Err(()),
         }
     }
@@ -231,9 +236,10 @@ impl AutoDiffAttrs {
         self.ret_activity == DiffActivity::ActiveOnly
     }
 
-    pub fn error() -> Self {
+    pub const fn error() -> Self {
         AutoDiffAttrs {
             mode: DiffMode::Error,
+            width: 0,
             ret_activity: DiffActivity::None,
             input_activity: Vec::new(),
         }
@@ -241,6 +247,7 @@ impl AutoDiffAttrs {
     pub fn source() -> Self {
         AutoDiffAttrs {
             mode: DiffMode::Source,
+            width: 0,
             ret_activity: DiffActivity::None,
             input_activity: Vec::new(),
         }
diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs
index 04c816293236..323a8fab6d59 100644
--- a/compiler/rustc_ast/src/expand/mod.rs
+++ b/compiler/rustc_ast/src/expand/mod.rs
@@ -13,12 +13,12 @@ pub mod typetree;
 #[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
 pub struct StrippedCfgItem<ModId = DefId> {
     pub parent_module: ModId,
-    pub name: Ident,
+    pub ident: Ident,
     pub cfg: MetaItem,
 }
 
 impl<ModId> StrippedCfgItem<ModId> {
     pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
-        StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg }
+        StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg }
     }
 }
diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs
index b93846c1fe6f..b611ddea1d9f 100644
--- a/compiler/rustc_ast/src/format.rs
+++ b/compiler/rustc_ast/src/format.rs
@@ -266,7 +266,7 @@ pub enum FormatAlignment {
 #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
 pub enum FormatCount {
     /// `{:5}` or `{:.5}`
-    Literal(usize),
+    Literal(u16),
     /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
     Argument(FormatArgPosition),
 }
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 6372c66050e7..294c6c9ba7a5 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -19,7 +19,6 @@
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
 #![feature(stmt_expr_attributes)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod util {
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index aa88e8369d5b..6aae2e481a59 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -9,10 +9,10 @@
 
 use std::ops::DerefMut;
 use std::panic;
+use std::sync::Arc;
 
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_data_structures::sync::Lrc;
 use rustc_span::source_map::Spanned;
 use rustc_span::{Ident, Span};
 use smallvec::{Array, SmallVec, smallvec};
@@ -41,7 +41,6 @@ pub trait WalkItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut impl MutVisitor,
@@ -143,6 +142,10 @@ pub trait MutVisitor: Sized {
         walk_flat_map_assoc_item(self, i, ctxt)
     }
 
+    fn visit_contract(&mut self, c: &mut P<FnContract>) {
+        walk_contract(self, c);
+    }
+
     fn visit_fn_decl(&mut self, d: &mut P<FnDecl>) {
         walk_fn_decl(self, d);
     }
@@ -206,6 +209,10 @@ pub trait MutVisitor: Sized {
         walk_ty(self, t);
     }
 
+    fn visit_ty_pat(&mut self, t: &mut P<TyPat>) {
+        walk_ty_pat(self, t);
+    }
+
     fn visit_lifetime(&mut self, l: &mut Lifetime) {
         walk_lifetime(self, l);
     }
@@ -230,6 +237,10 @@ pub trait MutVisitor: Sized {
         walk_ident(self, i);
     }
 
+    fn visit_modifiers(&mut self, m: &mut TraitBoundModifiers) {
+        walk_modifiers(self, m);
+    }
+
     fn visit_path(&mut self, p: &mut Path) {
         walk_path(self, p);
     }
@@ -330,8 +341,11 @@ pub trait MutVisitor: Sized {
         walk_where_clause(self, where_clause);
     }
 
-    fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) {
-        walk_where_predicate(self, where_predicate)
+    fn flat_map_where_predicate(
+        &mut self,
+        where_predicate: WherePredicate,
+    ) -> SmallVec<[WherePredicate; 1]> {
+        walk_flat_map_where_predicate(self, where_predicate)
     }
 
     fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) {
@@ -566,7 +580,7 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
         TyKind::Paren(ty) => vis.visit_ty(ty),
         TyKind::Pat(ty, pat) => {
             vis.visit_ty(ty);
-            vis.visit_pat(pat);
+            vis.visit_ty_pat(pat);
         }
         TyKind::Path(qself, path) => {
             vis.visit_qself(qself);
@@ -590,6 +604,20 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
     vis.visit_span(span);
 }
 
+pub fn walk_ty_pat<T: MutVisitor>(vis: &mut T, ty: &mut P<TyPat>) {
+    let TyPat { id, kind, span, tokens } = ty.deref_mut();
+    vis.visit_id(id);
+    match kind {
+        TyPatKind::Range(start, end, _include_end) => {
+            visit_opt(start, |c| vis.visit_anon_const(c));
+            visit_opt(end, |c| vis.visit_anon_const(c));
+        }
+        TyPatKind::Err(_) => {}
+    }
+    visit_lazy_tts(vis, tokens);
+    vis.visit_span(span);
+}
+
 fn walk_foreign_mod<T: MutVisitor>(vis: &mut T, foreign_mod: &mut ForeignMod) {
     let ForeignMod { extern_span: _, safety, abi: _, items } = foreign_mod;
     visit_safety(vis, safety);
@@ -676,7 +704,8 @@ fn walk_parenthesized_parameter_data<T: MutVisitor>(vis: &mut T, args: &mut Pare
 }
 
 fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut P<Local>) {
-    let Local { id, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
+    let Local { id, super_, pat, ty, kind, span, colon_sp, attrs, tokens } = local.deref_mut();
+    visit_opt(super_, |sp| vis.visit_span(sp));
     vis.visit_id(id);
     visit_attrs(vis, attrs);
     vis.visit_pat(pat);
@@ -789,14 +818,14 @@ fn visit_tt<T: MutVisitor>(vis: &mut T, tt: &mut TokenTree) {
 // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
 fn visit_tts<T: MutVisitor>(vis: &mut T, TokenStream(tts): &mut TokenStream) {
     if T::VISIT_TOKENS && !tts.is_empty() {
-        let tts = Lrc::make_mut(tts);
+        let tts = Arc::make_mut(tts);
         visit_vec(tts, |tree| visit_tt(vis, tree));
     }
 }
 
 fn visit_attr_tts<T: MutVisitor>(vis: &mut T, AttrTokenStream(tts): &mut AttrTokenStream) {
     if T::VISIT_TOKENS && !tts.is_empty() {
-        let tts = Lrc::make_mut(tts);
+        let tts = Arc::make_mut(tts);
         visit_vec(tts, |tree| visit_attr_tt(vis, tree));
     }
 }
@@ -815,9 +844,9 @@ fn visit_lazy_tts<T: MutVisitor>(vis: &mut T, lazy_tts: &mut Option<LazyAttrToke
     visit_lazy_tts_opt_mut(vis, lazy_tts.as_mut());
 }
 
-/// Applies ident visitor if it's an ident; applies other visits to interpolated nodes.
-/// In practice the ident part is not actually used by specific visitors right now,
-/// but there's a test below checking that it works.
+/// Applies ident visitor if it's an ident. In practice this is not actually
+/// used by specific visitors right now, but there's a test below checking that
+/// it works.
 // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
 pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
     let Token { kind, span } = t;
@@ -835,69 +864,11 @@ pub fn visit_token<T: MutVisitor>(vis: &mut T, t: &mut Token) {
         token::NtLifetime(ident, _is_raw) => {
             vis.visit_ident(ident);
         }
-        token::Interpolated(nt) => {
-            let nt = Lrc::make_mut(nt);
-            visit_nonterminal(vis, nt);
-        }
         _ => {}
     }
     vis.visit_span(span);
 }
 
-// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
-/// Applies the visitor to elements of interpolated nodes.
-//
-// N.B., this can occur only when applying a visitor to partially expanded
-// code, where parsed pieces have gotten implanted ito *other* macro
-// invocations. This is relevant for macro hygiene, but possibly not elsewhere.
-//
-// One problem here occurs because the types for flat_map_item, flat_map_stmt,
-// etc., allow the visitor to return *multiple* items; this is a problem for the
-// nodes here, because they insist on having exactly one piece. One solution
-// would be to mangle the MutVisitor trait to include one-to-many and
-// one-to-one versions of these entry points, but that would probably confuse a
-// lot of people and help very few. Instead, I'm just going to put in dynamic
-// checks. I think the performance impact of this will be pretty much
-// nonexistent. The danger is that someone will apply a `MutVisitor` to a
-// partially expanded node, and will be confused by the fact that their
-// `flat_map_item` or `flat_map_stmt` isn't getting called on `NtItem` or `NtStmt`
-// nodes. Hopefully they'll wind up reading this comment, and doing something
-// appropriate.
-//
-// BTW, design choice: I considered just changing the type of, e.g., `NtItem` to
-// contain multiple items, but decided against it when I looked at
-// `parse_item_or_view_item` and tried to figure out what I would do with
-// multiple items there....
-fn visit_nonterminal<T: MutVisitor>(vis: &mut T, nt: &mut token::Nonterminal) {
-    match nt {
-        token::NtItem(item) => visit_clobber(item, |item| {
-            // This is probably okay, because the only visitors likely to
-            // peek inside interpolated nodes will be renamings/markings,
-            // which map single items to single items.
-            vis.flat_map_item(item).expect_one("expected visitor to produce exactly one item")
-        }),
-        token::NtBlock(block) => vis.visit_block(block),
-        token::NtStmt(stmt) => visit_clobber(stmt, |stmt| {
-            // See reasoning above.
-            stmt.map(|stmt| {
-                vis.flat_map_stmt(stmt).expect_one("expected visitor to produce exactly one item")
-            })
-        }),
-        token::NtPat(pat) => vis.visit_pat(pat),
-        token::NtExpr(expr) => vis.visit_expr(expr),
-        token::NtTy(ty) => vis.visit_ty(ty),
-        token::NtLiteral(expr) => vis.visit_expr(expr),
-        token::NtMeta(item) => {
-            let AttrItem { unsafety: _, path, args, tokens } = item.deref_mut();
-            vis.visit_path(path);
-            visit_attr_args(vis, args);
-            visit_lazy_tts(vis, tokens);
-        }
-        token::NtPath(path) => vis.visit_path(path),
-        token::NtVis(visib) => vis.visit_vis(visib),
-    }
-}
-
 // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
 fn visit_defaultness<T: MutVisitor>(vis: &mut T, defaultness: &mut Defaultness) {
     match defaultness {
@@ -954,15 +925,34 @@ fn walk_coroutine_kind<T: MutVisitor>(vis: &mut T, coroutine_kind: &mut Coroutin
 
 fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
     match kind {
-        FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span }, _visibility, generics, body) => {
-            // Identifier and visibility are visited as a part of the item.
+        FnKind::Fn(
+            _ctxt,
+            _vis,
+            Fn {
+                defaultness,
+                ident,
+                generics,
+                contract,
+                body,
+                sig: FnSig { header, decl, span },
+                define_opaque,
+            },
+        ) => {
+            // Visibility is visited as a part of the item.
+            visit_defaultness(vis, defaultness);
+            vis.visit_ident(ident);
             vis.visit_fn_header(header);
             vis.visit_generics(generics);
             vis.visit_fn_decl(decl);
+            if let Some(contract) = contract {
+                vis.visit_contract(contract);
+            }
             if let Some(body) = body {
                 vis.visit_block(body);
             }
             vis.visit_span(span);
+
+            walk_define_opaques(vis, define_opaque);
         }
         FnKind::Closure(binder, coroutine_kind, decl, body) => {
             vis.visit_closure_binder(binder);
@@ -973,6 +963,16 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
     }
 }
 
+fn walk_contract<T: MutVisitor>(vis: &mut T, contract: &mut P<FnContract>) {
+    let FnContract { requires, ensures } = contract.deref_mut();
+    if let Some(pred) = requires {
+        vis.visit_expr(pred);
+    }
+    if let Some(pred) = ensures {
+        vis.visit_expr(pred);
+    }
+}
+
 fn walk_fn_decl<T: MutVisitor>(vis: &mut T, decl: &mut P<FnDecl>) {
     let FnDecl { inputs, output } = decl.deref_mut();
     inputs.flat_map_in_place(|param| vis.flat_map_param(param));
@@ -1066,15 +1066,20 @@ fn walk_ty_alias_where_clauses<T: MutVisitor>(vis: &mut T, tawcs: &mut TyAliasWh
 
 fn walk_where_clause<T: MutVisitor>(vis: &mut T, wc: &mut WhereClause) {
     let WhereClause { has_where_token: _, predicates, span } = wc;
-    visit_thin_vec(predicates, |predicate| vis.visit_where_predicate(predicate));
+    predicates.flat_map_in_place(|predicate| vis.flat_map_where_predicate(predicate));
     vis.visit_span(span);
 }
 
-pub fn walk_where_predicate<T: MutVisitor>(vis: &mut T, pred: &mut WherePredicate) {
-    let WherePredicate { kind, id, span } = pred;
+pub fn walk_flat_map_where_predicate<T: MutVisitor>(
+    vis: &mut T,
+    mut pred: WherePredicate,
+) -> SmallVec<[WherePredicate; 1]> {
+    let WherePredicate { attrs, kind, id, span, is_placeholder: _ } = &mut pred;
     vis.visit_id(id);
+    visit_attrs(vis, attrs);
     vis.visit_where_predicate_kind(kind);
     vis.visit_span(span);
+    smallvec![pred]
 }
 
 pub fn walk_where_predicate_kind<T: MutVisitor>(vis: &mut T, kind: &mut WherePredicateKind) {
@@ -1117,12 +1122,29 @@ fn walk_trait_ref<T: MutVisitor>(vis: &mut T, TraitRef { path, ref_id }: &mut Tr
 }
 
 fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
-    let PolyTraitRef { bound_generic_params, modifiers: _, trait_ref, span } = p;
+    let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span } = p;
+    vis.visit_modifiers(modifiers);
     bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param));
     vis.visit_trait_ref(trait_ref);
     vis.visit_span(span);
 }
 
+fn walk_modifiers<V: MutVisitor>(vis: &mut V, m: &mut TraitBoundModifiers) {
+    let TraitBoundModifiers { constness, asyncness, polarity } = m;
+    match constness {
+        BoundConstness::Never => {}
+        BoundConstness::Always(span) | BoundConstness::Maybe(span) => vis.visit_span(span),
+    }
+    match asyncness {
+        BoundAsyncness::Normal => {}
+        BoundAsyncness::Async(span) => vis.visit_span(span),
+    }
+    match polarity {
+        BoundPolarity::Positive => {}
+        BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => vis.visit_span(span),
+    }
+}
+
 pub fn walk_field_def<T: MutVisitor>(visitor: &mut T, fd: &mut FieldDef) {
     let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety, default } = fd;
     visitor.visit_id(id);
@@ -1165,7 +1187,7 @@ fn walk_mt<T: MutVisitor>(vis: &mut T, MutTy { ty, mutbl: _ }: &mut MutTy) {
 }
 
 pub fn walk_block<T: MutVisitor>(vis: &mut T, block: &mut P<Block>) {
-    let Block { id, stmts, rules: _, span, tokens, could_be_bare_literal: _ } = block.deref_mut();
+    let Block { id, stmts, rules: _, span, tokens } = block.deref_mut();
     vis.visit_id(id);
     stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
     visit_lazy_tts(vis, tokens);
@@ -1176,12 +1198,11 @@ pub fn walk_item_kind<K: WalkItemKind>(
     kind: &mut K,
     span: Span,
     id: NodeId,
-    ident: &mut Ident,
     visibility: &mut Visibility,
     ctxt: K::Ctxt,
     vis: &mut impl MutVisitor,
 ) {
-    kind.walk(span, id, ident, visibility, ctxt, vis)
+    kind.walk(span, id, visibility, ctxt, vis)
 }
 
 impl WalkItemKind for ItemKind {
@@ -1190,31 +1211,35 @@ impl WalkItemKind for ItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         _ctxt: Self::Ctxt,
         vis: &mut impl MutVisitor,
     ) {
         match self {
-            ItemKind::ExternCrate(_orig_name) => {}
+            ItemKind::ExternCrate(_orig_name, ident) => vis.visit_ident(ident),
             ItemKind::Use(use_tree) => vis.visit_use_tree(use_tree),
-            ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => {
+            ItemKind::Static(box StaticItem {
+                ident,
+                ty,
+                safety: _,
+                mutability: _,
+                expr,
+                define_opaque,
+            }) => {
+                vis.visit_ident(ident);
                 vis.visit_ty(ty);
                 visit_opt(expr, |expr| vis.visit_expr(expr));
+                walk_define_opaques(vis, define_opaque);
             }
             ItemKind::Const(item) => {
-                visit_const_item(item, vis);
+                walk_const_item(vis, item);
             }
-            ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
-                visit_defaultness(vis, defaultness);
-                vis.visit_fn(
-                    FnKind::Fn(FnCtxt::Free, ident, sig, visibility, generics, body),
-                    span,
-                    id,
-                );
+            ItemKind::Fn(func) => {
+                vis.visit_fn(FnKind::Fn(FnCtxt::Free, visibility, &mut *func), span, id);
             }
-            ItemKind::Mod(safety, mod_kind) => {
+            ItemKind::Mod(safety, ident, mod_kind) => {
                 visit_safety(vis, safety);
+                vis.visit_ident(ident);
                 match mod_kind {
                     ModKind::Loaded(
                         items,
@@ -1231,18 +1256,29 @@ impl WalkItemKind for ItemKind {
             }
             ItemKind::ForeignMod(nm) => vis.visit_foreign_mod(nm),
             ItemKind::GlobalAsm(asm) => vis.visit_inline_asm(asm),
-            ItemKind::TyAlias(box TyAlias { defaultness, generics, where_clauses, bounds, ty }) => {
+            ItemKind::TyAlias(box TyAlias {
+                defaultness,
+                ident,
+                generics,
+                where_clauses,
+                bounds,
+                ty,
+            }) => {
                 visit_defaultness(vis, defaultness);
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 visit_bounds(vis, bounds, BoundKind::Bound);
                 visit_opt(ty, |ty| vis.visit_ty(ty));
                 walk_ty_alias_where_clauses(vis, where_clauses);
             }
-            ItemKind::Enum(EnumDef { variants }, generics) => {
+            ItemKind::Enum(ident, EnumDef { variants }, generics) => {
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 variants.flat_map_in_place(|variant| vis.flat_map_variant(variant));
             }
-            ItemKind::Struct(variant_data, generics) | ItemKind::Union(variant_data, generics) => {
+            ItemKind::Struct(ident, variant_data, generics)
+            | ItemKind::Union(ident, variant_data, generics) => {
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 vis.visit_variant_data(variant_data);
             }
@@ -1263,24 +1299,32 @@ impl WalkItemKind for ItemKind {
                 visit_polarity(vis, polarity);
                 visit_opt(of_trait, |trait_ref| vis.visit_trait_ref(trait_ref));
                 vis.visit_ty(self_ty);
-                items.flat_map_in_place(|item| vis.flat_map_assoc_item(item, AssocCtxt::Impl));
+                items.flat_map_in_place(|item| {
+                    vis.flat_map_assoc_item(item, AssocCtxt::Impl { of_trait: of_trait.is_some() })
+                });
             }
-            ItemKind::Trait(box Trait { safety, is_auto: _, generics, bounds, items }) => {
+            ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => {
                 visit_safety(vis, safety);
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 visit_bounds(vis, bounds, BoundKind::Bound);
                 items.flat_map_in_place(|item| vis.flat_map_assoc_item(item, AssocCtxt::Trait));
             }
-            ItemKind::TraitAlias(generics, bounds) => {
+            ItemKind::TraitAlias(ident, generics, bounds) => {
+                vis.visit_ident(ident);
                 vis.visit_generics(generics);
                 visit_bounds(vis, bounds, BoundKind::Bound);
             }
             ItemKind::MacCall(m) => vis.visit_mac_call(m),
-            ItemKind::MacroDef(def) => vis.visit_macro_def(def),
+            ItemKind::MacroDef(ident, def) => {
+                vis.visit_ident(ident);
+                vis.visit_macro_def(def)
+            }
             ItemKind::Delegation(box Delegation {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
@@ -1288,6 +1332,7 @@ impl WalkItemKind for ItemKind {
                 vis.visit_id(id);
                 vis.visit_qself(qself);
                 vis.visit_path(path);
+                vis.visit_ident(ident);
                 if let Some(rename) = rename {
                     vis.visit_ident(rename);
                 }
@@ -1320,31 +1365,27 @@ impl WalkItemKind for AssocItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut impl MutVisitor,
     ) {
         match self {
             AssocItemKind::Const(item) => {
-                visit_const_item(item, visitor);
+                walk_const_item(visitor, item);
             }
-            AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
-                visit_defaultness(visitor, defaultness);
-                visitor.visit_fn(
-                    FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, visibility, generics, body),
-                    span,
-                    id,
-                );
+            AssocItemKind::Fn(func) => {
+                visitor.visit_fn(FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &mut *func), span, id);
             }
             AssocItemKind::Type(box TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 visit_defaultness(visitor, defaultness);
+                visitor.visit_ident(ident);
                 visitor.visit_generics(generics);
                 visit_bounds(visitor, bounds, BoundKind::Bound);
                 visit_opt(ty, |ty| visitor.visit_ty(ty));
@@ -1355,6 +1396,7 @@ impl WalkItemKind for AssocItemKind {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
@@ -1362,6 +1404,7 @@ impl WalkItemKind for AssocItemKind {
                 visitor.visit_id(id);
                 visitor.visit_qself(qself);
                 visitor.visit_path(path);
+                visitor.visit_ident(ident);
                 if let Some(rename) = rename {
                     visitor.visit_ident(rename);
                 }
@@ -1388,14 +1431,14 @@ impl WalkItemKind for AssocItemKind {
     }
 }
 
-fn visit_const_item<T: MutVisitor>(
-    ConstItem { defaultness, generics, ty, expr }: &mut ConstItem,
-    visitor: &mut T,
-) {
-    visit_defaultness(visitor, defaultness);
-    visitor.visit_generics(generics);
-    visitor.visit_ty(ty);
-    visit_opt(expr, |expr| visitor.visit_expr(expr));
+fn walk_const_item<T: MutVisitor>(vis: &mut T, item: &mut ConstItem) {
+    let ConstItem { defaultness, ident, generics, ty, expr, define_opaque } = item;
+    visit_defaultness(vis, defaultness);
+    vis.visit_ident(ident);
+    vis.visit_generics(generics);
+    vis.visit_ty(ty);
+    visit_opt(expr, |expr| vis.visit_expr(expr));
+    walk_define_opaques(vis, define_opaque);
 }
 
 fn walk_fn_header<T: MutVisitor>(vis: &mut T, header: &mut FnHeader) {
@@ -1428,12 +1471,11 @@ fn walk_item_ctxt<K: WalkItemKind>(
     item: &mut P<Item<K>>,
     ctxt: K::Ctxt,
 ) {
-    let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
+    let Item { attrs, id, kind, vis, span, tokens } = item.deref_mut();
     visitor.visit_id(id);
     visit_attrs(visitor, attrs);
     visitor.visit_vis(vis);
-    visitor.visit_ident(ident);
-    kind.walk(*span, *id, ident, vis, ctxt, visitor);
+    kind.walk(*span, *id, vis, ctxt, visitor);
     visit_lazy_tts(visitor, tokens);
     visitor.visit_span(span);
 }
@@ -1466,32 +1508,37 @@ impl WalkItemKind for ForeignItemKind {
         &mut self,
         span: Span,
         id: NodeId,
-        ident: &mut Ident,
         visibility: &mut Visibility,
         _ctxt: Self::Ctxt,
         visitor: &mut impl MutVisitor,
     ) {
         match self {
-            ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => {
+            ForeignItemKind::Static(box StaticItem {
+                ident,
+                ty,
+                mutability: _,
+                expr,
+                safety: _,
+                define_opaque,
+            }) => {
+                visitor.visit_ident(ident);
                 visitor.visit_ty(ty);
                 visit_opt(expr, |expr| visitor.visit_expr(expr));
+                walk_define_opaques(visitor, define_opaque);
             }
-            ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
-                visit_defaultness(visitor, defaultness);
-                visitor.visit_fn(
-                    FnKind::Fn(FnCtxt::Foreign, ident, sig, visibility, generics, body),
-                    span,
-                    id,
-                );
+            ForeignItemKind::Fn(func) => {
+                visitor.visit_fn(FnKind::Fn(FnCtxt::Foreign, visibility, &mut *func), span, id);
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 visit_defaultness(visitor, defaultness);
+                visitor.visit_ident(ident);
                 visitor.visit_generics(generics);
                 visit_bounds(visitor, bounds, BoundKind::Bound);
                 visit_opt(ty, |ty| visitor.visit_ty(ty));
@@ -1507,7 +1554,7 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
     vis.visit_id(id);
     match kind {
         PatKind::Err(_guar) => {}
-        PatKind::Wild | PatKind::Rest | PatKind::Never => {}
+        PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
         PatKind::Ident(_binding_mode, ident, sub) => {
             vis.visit_ident(ident);
             visit_opt(sub, |sub| vis.visit_pat(sub));
@@ -1713,6 +1760,10 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
             vis.visit_expr(expr);
             vis.visit_span(await_kw_span);
         }
+        ExprKind::Use(expr, use_kw_span) => {
+            vis.visit_expr(expr);
+            vis.visit_span(use_kw_span);
+        }
         ExprKind::Assign(el, er, span) => {
             vis.visit_expr(el);
             vis.visit_expr(er);
@@ -1777,8 +1828,11 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
         ExprKind::Paren(expr) => {
             vis.visit_expr(expr);
         }
-        ExprKind::Yield(expr) => {
-            visit_opt(expr, |expr| vis.visit_expr(expr));
+        ExprKind::Yield(kind) => {
+            let expr = kind.expr_mut();
+            if let Some(expr) = expr {
+                vis.visit_expr(expr);
+            }
         }
         ExprKind::Try(expr) => vis.visit_expr(expr),
         ExprKind::TryBlock(body) => vis.visit_block(body),
@@ -1813,10 +1867,10 @@ pub fn walk_flat_map_stmt<T: MutVisitor>(
         .into_iter()
         .map(|kind| Stmt { id, kind, span })
         .collect();
-    match stmts.len() {
-        0 => {}
-        1 => vis.visit_span(&mut stmts[0].span),
-        2.. => panic!(
+    match &mut stmts[..] {
+        [] => {}
+        [stmt] => vis.visit_span(&mut stmt.span),
+        _ => panic!(
             "cloning statement `NodeId`s is prohibited by default, \
              the visitor should implement custom statement visiting"
         ),
@@ -1863,6 +1917,21 @@ fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
         CaptureBy::Value { move_kw } => {
             vis.visit_span(move_kw);
         }
+        CaptureBy::Use { use_kw } => {
+            vis.visit_span(use_kw);
+        }
+    }
+}
+
+fn walk_define_opaques<T: MutVisitor>(
+    vis: &mut T,
+    define_opaque: &mut Option<ThinVec<(NodeId, Path)>>,
+) {
+    if let Some(define_opaque) = define_opaque {
+        for (id, path) in define_opaque {
+            vis.visit_id(id);
+            vis.visit_path(path)
+        }
     }
 }
 
@@ -1897,8 +1966,7 @@ impl DummyAstNode for Item {
                 span: Default::default(),
                 tokens: Default::default(),
             },
-            ident: Ident::empty(),
-            kind: ItemKind::ExternCrate(None),
+            kind: ItemKind::ExternCrate(None, Ident::dummy()),
             tokens: Default::default(),
         }
     }
@@ -1965,14 +2033,7 @@ impl<N: DummyAstNode, T: DummyAstNode> DummyAstNode for crate::ast_traits::AstNo
 #[derive(Debug)]
 pub enum FnKind<'a> {
     /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
-    Fn(
-        FnCtxt,
-        &'a mut Ident,
-        &'a mut FnSig,
-        &'a mut Visibility,
-        &'a mut Generics,
-        &'a mut Option<P<Block>>,
-    ),
+    Fn(FnCtxt, &'a mut Visibility, &'a mut Fn),
 
     /// E.g., `|x, y| body`.
     Closure(
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 3b7367d1ee20..055481f5d878 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -1,14 +1,10 @@
 use std::borrow::Cow;
 use std::fmt;
 
-pub use BinOpToken::*;
 pub use LitKind::*;
-pub use Nonterminal::*;
 pub use NtExprKind::*;
 pub use NtPatKind::*;
 pub use TokenKind::*;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::Lrc;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::edition::Edition;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
@@ -17,7 +13,6 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
 use rustc_span::{Ident, Symbol};
 
 use crate::ast;
-use crate::ptr::P;
 use crate::util::case::Case;
 
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
@@ -26,21 +21,6 @@ pub enum CommentKind {
     Block,
 }
 
-#[derive(Clone, PartialEq, Encodable, Decodable, Hash, Debug, Copy)]
-#[derive(HashStable_Generic)]
-pub enum BinOpToken {
-    Plus,
-    Minus,
-    Star,
-    Slash,
-    Percent,
-    Caret,
-    And,
-    Or,
-    Shl,
-    Shr,
-}
-
 // This type must not implement `Hash` due to the unusual `PartialEq` impl below.
 #[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)]
 pub enum InvisibleOrigin {
@@ -50,10 +30,6 @@ pub enum InvisibleOrigin {
     // Converted from `proc_macro::Delimiter` in
     // `proc_macro::Delimiter::to_internal`, i.e. returned by a proc macro.
     ProcMacro,
-
-    // Converted from `TokenKind::Interpolated` in
-    // `TokenStream::flatten_token`. Treated similarly to `ProcMacro`.
-    FlattenToken,
 }
 
 impl PartialEq for InvisibleOrigin {
@@ -84,11 +60,16 @@ pub enum MetaVarKind {
         // This field is needed for `Token::can_begin_string_literal`.
         can_begin_string_literal: bool,
     },
-    Ty,
+    Ty {
+        is_path: bool,
+    },
     Ident,
     Lifetime,
     Literal,
-    Meta,
+    Meta {
+        /// Will `AttrItem::meta` succeed on this, if reparsed?
+        has_meta_form: bool,
+    },
     Path,
     Vis,
     TT,
@@ -104,11 +85,11 @@ impl fmt::Display for MetaVarKind {
             MetaVarKind::Pat(PatParam { inferred: false }) => sym::pat_param,
             MetaVarKind::Expr { kind: Expr2021 { inferred: true } | Expr, .. } => sym::expr,
             MetaVarKind::Expr { kind: Expr2021 { inferred: false }, .. } => sym::expr_2021,
-            MetaVarKind::Ty => sym::ty,
+            MetaVarKind::Ty { .. } => sym::ty,
             MetaVarKind::Ident => sym::ident,
             MetaVarKind::Lifetime => sym::lifetime,
             MetaVarKind::Literal => sym::literal,
-            MetaVarKind::Meta => sym::meta,
+            MetaVarKind::Meta { .. } => sym::meta,
             MetaVarKind::Path => sym::path,
             MetaVarKind::Vis => sym::vis,
             MetaVarKind::TT => sym::tt,
@@ -145,9 +126,7 @@ impl Delimiter {
         match self {
             Delimiter::Parenthesis | Delimiter::Bracket | Delimiter::Brace => false,
             Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) => false,
-            Delimiter::Invisible(InvisibleOrigin::FlattenToken | InvisibleOrigin::ProcMacro) => {
-                true
-            }
+            Delimiter::Invisible(InvisibleOrigin::ProcMacro) => true,
         }
     }
 
@@ -209,16 +188,17 @@ impl Lit {
         }
     }
 
-    /// Keep this in sync with `Token::can_begin_literal_maybe_minus` excluding unary negation.
+    /// Keep this in sync with `Token::can_begin_literal_maybe_minus` and
+    /// `Parser::eat_token_lit` (excluding unary negation).
     pub fn from_token(token: &Token) -> Option<Lit> {
         match token.uninterpolate().kind {
             Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
             Literal(token_lit) => Some(token_lit),
-            Interpolated(ref nt)
-                if let NtExpr(expr) | NtLiteral(expr) = &**nt
-                    && let ast::ExprKind::Lit(token_lit) = expr.kind =>
-            {
-                Some(token_lit)
+            OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
+                MetaVarKind::Literal | MetaVarKind::Expr { .. },
+            ))) => {
+                // Unreachable with the current test suite.
+                panic!("from_token metavar");
             }
             _ => None,
         }
@@ -347,9 +327,7 @@ impl From<IdentIsRaw> for bool {
     }
 }
 
-// SAFETY: due to the `Clone` impl below, all fields of all variants other than
-// `Interpolated` must impl `Copy`.
-#[derive(PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub enum TokenKind {
     /* Expression-operator symbols. */
     /// `=`
@@ -371,11 +349,49 @@ pub enum TokenKind {
     /// `||`
     OrOr,
     /// `!`
-    Not,
+    Bang,
     /// `~`
     Tilde,
-    BinOp(BinOpToken),
-    BinOpEq(BinOpToken),
+    // `+`
+    Plus,
+    // `-`
+    Minus,
+    // `*`
+    Star,
+    // `/`
+    Slash,
+    // `%`
+    Percent,
+    // `^`
+    Caret,
+    // `&`
+    And,
+    // `|`
+    Or,
+    // `<<`
+    Shl,
+    // `>>`
+    Shr,
+    // `+=`
+    PlusEq,
+    // `-=`
+    MinusEq,
+    // `*=`
+    StarEq,
+    // `/=`
+    SlashEq,
+    // `%=`
+    PercentEq,
+    // `^=`
+    CaretEq,
+    // `&=`
+    AndEq,
+    // `|=`
+    OrEq,
+    // `<<=`
+    ShlEq,
+    // `>>=`
+    ShrEq,
 
     /* Structural symbols */
     /// `@`
@@ -420,8 +436,9 @@ pub enum TokenKind {
 
     /// Identifier token.
     /// Do not forget about `NtIdent` when you want to match on identifiers.
-    /// It's recommended to use `Token::(ident,uninterpolate,uninterpolated_span)` to
-    /// treat regular and interpolated identifiers in the same way.
+    /// It's recommended to use `Token::{ident,uninterpolate}` and
+    /// `Parser::token_uninterpolated_span` to treat regular and interpolated
+    /// identifiers in the same way.
     Ident(Symbol, IdentIsRaw),
     /// This identifier (and its span) is the identifier passed to the
     /// declarative macro. The span in the surrounding `Token` is the span of
@@ -430,29 +447,15 @@ pub enum TokenKind {
 
     /// Lifetime identifier token.
     /// Do not forget about `NtLifetime` when you want to match on lifetime identifiers.
-    /// It's recommended to use `Token::(lifetime,uninterpolate,uninterpolated_span)` to
-    /// treat regular and interpolated lifetime identifiers in the same way.
+    /// It's recommended to use `Token::{ident,uninterpolate}` and
+    /// `Parser::token_uninterpolated_span` to treat regular and interpolated
+    /// identifiers in the same way.
     Lifetime(Symbol, IdentIsRaw),
     /// This identifier (and its span) is the lifetime passed to the
     /// declarative macro. The span in the surrounding `Token` is the span of
     /// the `lifetime` metavariable in the macro's RHS.
     NtLifetime(Ident, IdentIsRaw),
 
-    /// An embedded AST node, as produced by a macro. This only exists for
-    /// historical reasons. We'd like to get rid of it, for multiple reasons.
-    /// - It's conceptually very strange. Saying a token can contain an AST
-    ///   node is like saying, in natural language, that a word can contain a
-    ///   sentence.
-    /// - It requires special handling in a bunch of places in the parser.
-    /// - It prevents `Token` from implementing `Copy`.
-    /// It adds complexity and likely slows things down. Please don't add new
-    /// occurrences of this token kind!
-    ///
-    /// The span in the surrounding `Token` is that of the metavariable in the
-    /// macro's RHS. The span within the Nonterminal is that of the fragment
-    /// passed to the macro at the call site.
-    Interpolated(Lrc<Nonterminal>),
-
     /// A doc comment token.
     /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
     /// similarly to symbols in string literal tokens.
@@ -462,20 +465,7 @@ pub enum TokenKind {
     Eof,
 }
 
-impl Clone for TokenKind {
-    fn clone(&self) -> Self {
-        // `TokenKind` would impl `Copy` if it weren't for `Interpolated`. So
-        // for all other variants, this implementation of `clone` is just like
-        // a copy. This is faster than the `derive(Clone)` version which has a
-        // separate path for every variant.
-        match self {
-            Interpolated(nt) => Interpolated(Lrc::clone(nt)),
-            _ => unsafe { std::ptr::read(self) },
-        }
-    }
-}
-
-#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct Token {
     pub kind: TokenKind,
     pub span: Span,
@@ -495,31 +485,31 @@ impl TokenKind {
         Some(match (self, n) {
             (Le, 1) => (Lt, Eq),
             (EqEq, 1) => (Eq, Eq),
-            (Ne, 1) => (Not, Eq),
+            (Ne, 1) => (Bang, Eq),
             (Ge, 1) => (Gt, Eq),
-            (AndAnd, 1) => (BinOp(And), BinOp(And)),
-            (OrOr, 1) => (BinOp(Or), BinOp(Or)),
-            (BinOp(Shl), 1) => (Lt, Lt),
-            (BinOp(Shr), 1) => (Gt, Gt),
-            (BinOpEq(Plus), 1) => (BinOp(Plus), Eq),
-            (BinOpEq(Minus), 1) => (BinOp(Minus), Eq),
-            (BinOpEq(Star), 1) => (BinOp(Star), Eq),
-            (BinOpEq(Slash), 1) => (BinOp(Slash), Eq),
-            (BinOpEq(Percent), 1) => (BinOp(Percent), Eq),
-            (BinOpEq(Caret), 1) => (BinOp(Caret), Eq),
-            (BinOpEq(And), 1) => (BinOp(And), Eq),
-            (BinOpEq(Or), 1) => (BinOp(Or), Eq),
-            (BinOpEq(Shl), 1) => (Lt, Le),         // `<` + `<=`
-            (BinOpEq(Shl), 2) => (BinOp(Shl), Eq), // `<<` + `=`
-            (BinOpEq(Shr), 1) => (Gt, Ge),         // `>` + `>=`
-            (BinOpEq(Shr), 2) => (BinOp(Shr), Eq), // `>>` + `=`
+            (AndAnd, 1) => (And, And),
+            (OrOr, 1) => (Or, Or),
+            (Shl, 1) => (Lt, Lt),
+            (Shr, 1) => (Gt, Gt),
+            (PlusEq, 1) => (Plus, Eq),
+            (MinusEq, 1) => (Minus, Eq),
+            (StarEq, 1) => (Star, Eq),
+            (SlashEq, 1) => (Slash, Eq),
+            (PercentEq, 1) => (Percent, Eq),
+            (CaretEq, 1) => (Caret, Eq),
+            (AndEq, 1) => (And, Eq),
+            (OrEq, 1) => (Or, Eq),
+            (ShlEq, 1) => (Lt, Le),  // `<` + `<=`
+            (ShlEq, 2) => (Shl, Eq), // `<<` + `=`
+            (ShrEq, 1) => (Gt, Ge),  // `>` + `>=`
+            (ShrEq, 2) => (Shr, Eq), // `>>` + `=`
             (DotDot, 1) => (Dot, Dot),
             (DotDotDot, 1) => (Dot, DotDot), // `.` + `..`
             (DotDotDot, 2) => (DotDot, Dot), // `..` + `.`
             (DotDotEq, 2) => (DotDot, Eq),
             (PathSep, 1) => (Colon, Colon),
-            (RArrow, 1) => (BinOp(Minus), Gt),
-            (LArrow, 1) => (Lt, BinOp(Minus)),
+            (RArrow, 1) => (Minus, Gt),
+            (LArrow, 1) => (Lt, Minus),
             (FatArrow, 1) => (Eq, Gt),
             _ => return None,
         })
@@ -527,18 +517,18 @@ impl TokenKind {
 
     /// Returns tokens that are likely to be typed accidentally instead of the current token.
     /// Enables better error recovery when the wrong token is found.
-    pub fn similar_tokens(&self) -> Option<Vec<TokenKind>> {
-        match *self {
-            Comma => Some(vec![Dot, Lt, Semi]),
-            Semi => Some(vec![Colon, Comma]),
-            Colon => Some(vec![Semi]),
-            FatArrow => Some(vec![Eq, RArrow, Ge, Gt]),
-            _ => None,
+    pub fn similar_tokens(&self) -> &[TokenKind] {
+        match self {
+            Comma => &[Dot, Lt, Semi],
+            Semi => &[Colon, Comma],
+            Colon => &[Semi],
+            FatArrow => &[Eq, RArrow, Ge, Gt],
+            _ => &[],
         }
     }
 
     pub fn should_end_const_arg(&self) -> bool {
-        matches!(self, Gt | Ge | BinOp(Shr) | BinOpEq(Shr))
+        matches!(self, Gt | Ge | Shr | ShrEq)
     }
 }
 
@@ -557,39 +547,25 @@ impl Token {
         Token::new(Ident(ident.name, ident.is_raw_guess().into()), ident.span)
     }
 
-    /// For interpolated tokens, returns a span of the fragment to which the interpolated
-    /// token refers. For all other tokens this is just a regular span.
-    /// It is particularly important to use this for identifiers and lifetimes
-    /// for which spans affect name resolution and edition checks.
-    /// Note that keywords are also identifiers, so they should use this
-    /// if they keep spans or perform edition checks.
-    pub fn uninterpolated_span(&self) -> Span {
-        match self.kind {
-            NtIdent(ident, _) | NtLifetime(ident, _) => ident.span,
-            Interpolated(ref nt) => nt.use_span(),
-            _ => self.span,
-        }
-    }
-
     pub fn is_range_separator(&self) -> bool {
         [DotDot, DotDotDot, DotDotEq].contains(&self.kind)
     }
 
     pub fn is_punct(&self) -> bool {
         match self.kind {
-            Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Not | Tilde | BinOp(_)
-            | BinOpEq(_) | At | Dot | DotDot | DotDotDot | DotDotEq | Comma | Semi | Colon
-            | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | SingleQuote => {
-                true
-            }
+            Eq | Lt | Le | EqEq | Ne | Ge | Gt | AndAnd | OrOr | Bang | Tilde | Plus | Minus
+            | Star | Slash | Percent | Caret | And | Or | Shl | Shr | PlusEq | MinusEq | StarEq
+            | SlashEq | PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | Dot | DotDot
+            | DotDotDot | DotDotEq | Comma | Semi | Colon | PathSep | RArrow | LArrow
+            | FatArrow | Pound | Dollar | Question | SingleQuote => true,
 
             OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..)
-            | NtIdent(..) | Lifetime(..) | NtLifetime(..) | Interpolated(..) | Eof => false,
+            | NtIdent(..) | Lifetime(..) | NtLifetime(..) | Eof => false,
         }
     }
 
     pub fn is_like_plus(&self) -> bool {
-        matches!(self.kind, BinOp(Plus) | BinOpEq(Plus))
+        matches!(self.kind, Plus | PlusEq)
     }
 
     /// Returns `true` if the token can appear at the start of an expression.
@@ -603,25 +579,18 @@ impl Token {
                 ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
             OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block
             Literal(..)                       | // literal
-            Not                               | // operator not
-            BinOp(Minus)                      | // unary minus
-            BinOp(Star)                       | // dereference
-            BinOp(Or) | OrOr                  | // closure
-            BinOp(And)                        | // reference
+            Bang                              | // operator not
+            Minus                             | // unary minus
+            Star                              | // dereference
+            Or | OrOr                         | // closure
+            And                               | // reference
             AndAnd                            | // double reference
             // DotDotDot is no longer supported, but we need some way to display the error
             DotDot | DotDotDot | DotDotEq     | // range notation
-            Lt | BinOp(Shl)                   | // associated path
+            Lt | Shl                          | // associated path
             PathSep                           | // global path
             Lifetime(..)                      | // labeled loop
             Pound                             => true, // expression attributes
-            Interpolated(ref nt) =>
-                matches!(&**nt,
-                    NtBlock(..)   |
-                    NtExpr(..)    |
-                    NtLiteral(..) |
-                    NtPath(..)
-                ),
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
                 MetaVarKind::Block |
                 MetaVarKind::Expr { .. } |
@@ -641,33 +610,23 @@ impl Token {
             Ident(..) | NtIdent(..) |
             OpenDelim(Delimiter::Parenthesis) |  // tuple pattern
             OpenDelim(Delimiter::Bracket) |      // slice pattern
-            BinOp(And) |                  // reference
-            BinOp(Minus) |                // negative literal
-            AndAnd |                      // double reference
-            Literal(_) |                  // literal
-            DotDot |                      // range pattern (future compat)
-            DotDotDot |                   // range pattern (future compat)
-            PathSep |                     // path
-            Lt |                          // path (UFCS constant)
-            BinOp(Shl) => true,           // path (double UFCS)
-            // leading vert `|` or-pattern
-            BinOp(Or) => matches!(pat_kind, PatWithOr),
-            Interpolated(nt) =>
-                matches!(&**nt,
-                    | NtExpr(..)
-                    | NtLiteral(..)
-                    | NtMeta(..)
-                    | NtPat(..)
-                    | NtPath(..)
-                    | NtTy(..)
-                ),
+            And |                                // reference
+            Minus |                              // negative literal
+            AndAnd |                             // double reference
+            Literal(_) |                         // literal
+            DotDot |                             // range pattern (future compat)
+            DotDotDot |                          // range pattern (future compat)
+            PathSep |                            // path
+            Lt |                                 // path (UFCS constant)
+            Shl => true,                         // path (double UFCS)
+            Or => matches!(pat_kind, PatWithOr), // leading vert `|` or-pattern
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
                 MetaVarKind::Expr { .. } |
                 MetaVarKind::Literal |
-                MetaVarKind::Meta |
+                MetaVarKind::Meta { .. } |
                 MetaVarKind::Pat(_) |
                 MetaVarKind::Path |
-                MetaVarKind::Ty
+                MetaVarKind::Ty { .. }
             ))) => true,
             _ => false,
         }
@@ -676,21 +635,20 @@ impl Token {
     /// Returns `true` if the token can appear at the start of a type.
     pub fn can_begin_type(&self) -> bool {
         match self.uninterpolate().kind {
-            Ident(name, is_raw)        =>
+            Ident(name, is_raw) =>
                 ident_can_begin_type(name, self.span, is_raw), // type name or keyword
             OpenDelim(Delimiter::Parenthesis) | // tuple
             OpenDelim(Delimiter::Bracket)     | // array
-            Not                         | // never
-            BinOp(Star)                 | // raw pointer
-            BinOp(And)                  | // reference
-            AndAnd                      | // double reference
-            Question                    | // maybe bound in trait object
-            Lifetime(..)                | // lifetime bound in trait object
-            Lt | BinOp(Shl)             | // associated path
-            PathSep                      => true, // global path
-            Interpolated(ref nt) => matches!(&**nt, NtTy(..) | NtPath(..)),
+            Bang                              | // never
+            Star                              | // raw pointer
+            And                               | // reference
+            AndAnd                            | // double reference
+            Question                          | // maybe bound in trait object
+            Lifetime(..)                      | // lifetime bound in trait object
+            Lt | Shl                          | // associated path
+            PathSep => true,                    // global path
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
-                MetaVarKind::Ty |
+                MetaVarKind::Ty { .. } |
                 MetaVarKind::Path
             ))) => true,
             // For anonymous structs or unions, which only appear in specific positions
@@ -702,9 +660,8 @@ impl Token {
     /// Returns `true` if the token can appear at the start of a const param.
     pub fn can_begin_const_arg(&self) -> bool {
         match self.kind {
-            OpenDelim(Delimiter::Brace) | Literal(..) | BinOp(Minus) => true,
+            OpenDelim(Delimiter::Brace) | Literal(..) | Minus => true,
             Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
-            Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
                 MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal,
             ))) => true,
@@ -748,22 +705,12 @@ impl Token {
     ///
     /// In other words, would this token be a valid start of `parse_literal_maybe_minus`?
     ///
-    /// Keep this in sync with and `Lit::from_token`, excluding unary negation.
+    /// Keep this in sync with `Lit::from_token` and `Parser::eat_token_lit`
+    /// (excluding unary negation).
     pub fn can_begin_literal_maybe_minus(&self) -> bool {
         match self.uninterpolate().kind {
-            Literal(..) | BinOp(Minus) => true,
+            Literal(..) | Minus => true,
             Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
-            Interpolated(ref nt) => match &**nt {
-                NtLiteral(_) => true,
-                NtExpr(e) => match &e.kind {
-                    ast::ExprKind::Lit(_) => true,
-                    ast::ExprKind::Unary(ast::UnOp::Neg, e) => {
-                        matches!(&e.kind, ast::ExprKind::Lit(_))
-                    }
-                    _ => false,
-                },
-                _ => false,
-            },
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind {
                 MetaVarKind::Literal => true,
                 MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => {
@@ -778,14 +725,6 @@ impl Token {
     pub fn can_begin_string_literal(&self) -> bool {
         match self.uninterpolate().kind {
             Literal(..) => true,
-            Interpolated(ref nt) => match &**nt {
-                NtLiteral(_) => true,
-                NtExpr(e) => match &e.kind {
-                    ast::ExprKind::Lit(_) => true,
-                    _ => false,
-                },
-                _ => false,
-            },
             OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind {
                 MetaVarKind::Literal => true,
                 MetaVarKind::Expr { can_begin_string_literal, .. } => can_begin_string_literal,
@@ -847,38 +786,23 @@ impl Token {
         self.ident().is_some_and(|(ident, _)| ident.name == name)
     }
 
-    /// Returns `true` if the token is an interpolated path.
-    fn is_whole_path(&self) -> bool {
-        if let Interpolated(nt) = &self.kind
-            && let NtPath(..) = &**nt
-        {
-            return true;
-        }
-
-        false
-    }
-
     /// Is this a pre-parsed expression dropped into the token stream
     /// (which happens while parsing the result of macro expansion)?
-    pub fn is_whole_expr(&self) -> bool {
-        if let Interpolated(nt) = &self.kind
-            && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = &**nt
-        {
-            return true;
-        }
-
-        false
+    pub fn is_metavar_expr(&self) -> bool {
+        matches!(
+            self.is_metavar_seq(),
+            Some(
+                MetaVarKind::Expr { .. }
+                    | MetaVarKind::Literal
+                    | MetaVarKind::Path
+                    | MetaVarKind::Block
+            )
+        )
     }
 
-    /// Is the token an interpolated block (`$b:block`)?
-    pub fn is_whole_block(&self) -> bool {
-        if let Interpolated(nt) = &self.kind
-            && let NtBlock(..) = &**nt
-        {
-            return true;
-        }
-
-        false
+    /// Are we at a block from a metavar (`$b:block`)?
+    pub fn is_metavar_block(&self) -> bool {
+        matches!(self.is_metavar_seq(), Some(MetaVarKind::Block))
     }
 
     /// Returns `true` if the token is either the `mut` or `const` keyword.
@@ -887,13 +811,13 @@ impl Token {
     }
 
     pub fn is_qpath_start(&self) -> bool {
-        self == &Lt || self == &BinOp(Shl)
+        self == &Lt || self == &Shl
     }
 
     pub fn is_path_start(&self) -> bool {
         self == &PathSep
             || self.is_qpath_start()
-            || self.is_whole_path()
+            || matches!(self.is_metavar_seq(), Some(MetaVarKind::Path))
             || self.is_path_segment_keyword()
             || self.is_ident() && !self.is_reserved_ident()
     }
@@ -918,11 +842,6 @@ impl Token {
         self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
     }
 
-    /// Don't use this unless you're doing something very loose and heuristic-y.
-    pub fn is_any_keyword(&self) -> bool {
-        self.is_non_raw_ident_where(Ident::is_any_keyword)
-    }
-
     /// Returns true for reserved identifiers used internally for elided lifetimes,
     /// unnamed method parameters, crate root module, error recovery etc.
     pub fn is_special_ident(&self) -> bool {
@@ -969,60 +888,92 @@ impl Token {
         }
     }
 
+    /// Is this an invisible open delimiter at the start of a token sequence
+    /// from an expanded metavar?
+    pub fn is_metavar_seq(&self) -> Option<MetaVarKind> {
+        match self.kind {
+            OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => Some(kind),
+            _ => None,
+        }
+    }
+
     pub fn glue(&self, joint: &Token) -> Option<Token> {
-        let kind = match self.kind {
-            Eq => match joint.kind {
-                Eq => EqEq,
-                Gt => FatArrow,
-                _ => return None,
-            },
-            Lt => match joint.kind {
-                Eq => Le,
-                Lt => BinOp(Shl),
-                Le => BinOpEq(Shl),
-                BinOp(Minus) => LArrow,
-                _ => return None,
-            },
-            Gt => match joint.kind {
-                Eq => Ge,
-                Gt => BinOp(Shr),
-                Ge => BinOpEq(Shr),
-                _ => return None,
-            },
-            Not => match joint.kind {
-                Eq => Ne,
-                _ => return None,
-            },
-            BinOp(op) => match joint.kind {
-                Eq => BinOpEq(op),
-                BinOp(And) if op == And => AndAnd,
-                BinOp(Or) if op == Or => OrOr,
-                Gt if op == Minus => RArrow,
-                _ => return None,
-            },
-            Dot => match joint.kind {
-                Dot => DotDot,
-                DotDot => DotDotDot,
-                _ => return None,
-            },
-            DotDot => match joint.kind {
-                Dot => DotDotDot,
-                Eq => DotDotEq,
-                _ => return None,
-            },
-            Colon => match joint.kind {
-                Colon => PathSep,
-                _ => return None,
-            },
-            SingleQuote => match joint.kind {
-                Ident(name, is_raw) => Lifetime(Symbol::intern(&format!("'{name}")), is_raw),
-                _ => return None,
-            },
+        let kind = match (&self.kind, &joint.kind) {
+            (Eq, Eq) => EqEq,
+            (Eq, Gt) => FatArrow,
+            (Eq, _) => return None,
+
+            (Lt, Eq) => Le,
+            (Lt, Lt) => Shl,
+            (Lt, Le) => ShlEq,
+            (Lt, Minus) => LArrow,
+            (Lt, _) => return None,
+
+            (Gt, Eq) => Ge,
+            (Gt, Gt) => Shr,
+            (Gt, Ge) => ShrEq,
+            (Gt, _) => return None,
+
+            (Bang, Eq) => Ne,
+            (Bang, _) => return None,
+
+            (Plus, Eq) => PlusEq,
+            (Plus, _) => return None,
+
+            (Minus, Eq) => MinusEq,
+            (Minus, Gt) => RArrow,
+            (Minus, _) => return None,
+
+            (Star, Eq) => StarEq,
+            (Star, _) => return None,
+
+            (Slash, Eq) => SlashEq,
+            (Slash, _) => return None,
+
+            (Percent, Eq) => PercentEq,
+            (Percent, _) => return None,
+
+            (Caret, Eq) => CaretEq,
+            (Caret, _) => return None,
+
+            (And, Eq) => AndEq,
+            (And, And) => AndAnd,
+            (And, _) => return None,
+
+            (Or, Eq) => OrEq,
+            (Or, Or) => OrOr,
+            (Or, _) => return None,
 
-            Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot
-            | DotDotEq | Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar
-            | Question | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..)
-            | Lifetime(..) | NtLifetime(..) | Interpolated(..) | DocComment(..) | Eof => {
+            (Shl, Eq) => ShlEq,
+            (Shl, _) => return None,
+
+            (Shr, Eq) => ShrEq,
+            (Shr, _) => return None,
+
+            (Dot, Dot) => DotDot,
+            (Dot, DotDot) => DotDotDot,
+            (Dot, _) => return None,
+
+            (DotDot, Dot) => DotDotDot,
+            (DotDot, Eq) => DotDotEq,
+            (DotDot, _) => return None,
+
+            (Colon, Colon) => PathSep,
+            (Colon, _) => return None,
+
+            (SingleQuote, Ident(name, is_raw)) => {
+                Lifetime(Symbol::intern(&format!("'{name}")), *is_raw)
+            }
+            (SingleQuote, _) => return None,
+
+            (
+                Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | PlusEq | MinusEq | StarEq | SlashEq
+                | PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | DotDotDot | DotDotEq
+                | Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question
+                | OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..)
+                | Lifetime(..) | NtLifetime(..) | DocComment(..) | Eof,
+                _,
+            ) => {
                 return None;
             }
         };
@@ -1059,22 +1010,6 @@ pub enum NtExprKind {
     Expr2021 { inferred: bool },
 }
 
-#[derive(Clone, Encodable, Decodable)]
-/// For interpolation during macro expansion.
-pub enum Nonterminal {
-    NtItem(P<ast::Item>),
-    NtBlock(P<ast::Block>),
-    NtStmt(P<ast::Stmt>),
-    NtPat(P<ast::Pat>),
-    NtExpr(P<ast::Expr>),
-    NtTy(P<ast::Ty>),
-    NtLiteral(P<ast::Expr>),
-    /// Stuff inside brackets for attributes
-    NtMeta(P<ast::AttrItem>),
-    NtPath(P<ast::Path>),
-    NtVis(P<ast::Visibility>),
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
 pub enum NonterminalKind {
     Item,
@@ -1158,73 +1093,6 @@ impl fmt::Display for NonterminalKind {
     }
 }
 
-impl Nonterminal {
-    pub fn use_span(&self) -> Span {
-        match self {
-            NtItem(item) => item.span,
-            NtBlock(block) => block.span,
-            NtStmt(stmt) => stmt.span,
-            NtPat(pat) => pat.span,
-            NtExpr(expr) | NtLiteral(expr) => expr.span,
-            NtTy(ty) => ty.span,
-            NtMeta(attr_item) => attr_item.span(),
-            NtPath(path) => path.span,
-            NtVis(vis) => vis.span,
-        }
-    }
-
-    pub fn descr(&self) -> &'static str {
-        match self {
-            NtItem(..) => "item",
-            NtBlock(..) => "block",
-            NtStmt(..) => "statement",
-            NtPat(..) => "pattern",
-            NtExpr(..) => "expression",
-            NtLiteral(..) => "literal",
-            NtTy(..) => "type",
-            NtMeta(..) => "attribute",
-            NtPath(..) => "path",
-            NtVis(..) => "visibility",
-        }
-    }
-}
-
-impl PartialEq for Nonterminal {
-    fn eq(&self, _rhs: &Self) -> bool {
-        // FIXME: Assume that all nonterminals are not equal, we can't compare them
-        // correctly based on data from AST. This will prevent them from matching each other
-        // in macros. The comparison will become possible only when each nonterminal has an
-        // attached token stream from which it was parsed.
-        false
-    }
-}
-
-impl fmt::Debug for Nonterminal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            NtItem(..) => f.pad("NtItem(..)"),
-            NtBlock(..) => f.pad("NtBlock(..)"),
-            NtStmt(..) => f.pad("NtStmt(..)"),
-            NtPat(..) => f.pad("NtPat(..)"),
-            NtExpr(..) => f.pad("NtExpr(..)"),
-            NtTy(..) => f.pad("NtTy(..)"),
-            NtLiteral(..) => f.pad("NtLiteral(..)"),
-            NtMeta(..) => f.pad("NtMeta(..)"),
-            NtPath(..) => f.pad("NtPath(..)"),
-            NtVis(..) => f.pad("NtVis(..)"),
-        }
-    }
-}
-
-impl<CTX> HashStable<CTX> for Nonterminal
-where
-    CTX: crate::HashStableContext,
-{
-    fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) {
-        panic!("interpolated tokens should not be present in the HIR")
-    }
-}
-
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
@@ -1234,7 +1102,6 @@ mod size_asserts {
     // tidy-alphabetical-start
     static_assert_size!(Lit, 12);
     static_assert_size!(LitKind, 2);
-    static_assert_size!(Nonterminal, 16);
     static_assert_size!(Token, 24);
     static_assert_size!(TokenKind, 16);
     // tidy-alphabetical-end
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index e7b393d869d2..43d25d180750 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -14,17 +14,18 @@
 //! ownership of the original.
 
 use std::borrow::Cow;
+use std::sync::Arc;
 use std::{cmp, fmt, iter};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::{self, Lrc};
+use rustc_data_structures::sync;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_serialize::{Decodable, Encodable};
 use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym};
 
-use crate::ast::{AttrStyle, StmtKind};
+use crate::ast::AttrStyle;
 use crate::ast_traits::{HasAttrs, HasTokens};
-use crate::token::{self, Delimiter, InvisibleOrigin, Nonterminal, Token, TokenKind};
+use crate::token::{self, Delimiter, Token, TokenKind};
 use crate::{AttrVec, Attribute};
 
 /// Part of a `TokenStream`.
@@ -119,11 +120,11 @@ impl ToAttrTokenStream for AttrTokenStream {
 /// of an actual `TokenStream` until it is needed.
 /// `Box` is here only to reduce the structure size.
 #[derive(Clone)]
-pub struct LazyAttrTokenStream(Lrc<Box<dyn ToAttrTokenStream>>);
+pub struct LazyAttrTokenStream(Arc<Box<dyn ToAttrTokenStream>>);
 
 impl LazyAttrTokenStream {
     pub fn new(inner: impl ToAttrTokenStream + 'static) -> LazyAttrTokenStream {
-        LazyAttrTokenStream(Lrc::new(Box::new(inner)))
+        LazyAttrTokenStream(Arc::new(Box::new(inner)))
     }
 
     pub fn to_attr_token_stream(&self) -> AttrTokenStream {
@@ -160,7 +161,7 @@ impl<CTX> HashStable<CTX> for LazyAttrTokenStream {
 /// during expansion to perform early cfg-expansion, and to process attributes
 /// during proc-macro invocations.
 #[derive(Clone, Debug, Default, Encodable, Decodable)]
-pub struct AttrTokenStream(pub Lrc<Vec<AttrTokenTree>>);
+pub struct AttrTokenStream(pub Arc<Vec<AttrTokenTree>>);
 
 /// Like `TokenTree`, but for `AttrTokenStream`.
 #[derive(Clone, Debug, Encodable, Decodable)]
@@ -175,7 +176,7 @@ pub enum AttrTokenTree {
 
 impl AttrTokenStream {
     pub fn new(tokens: Vec<AttrTokenTree>) -> AttrTokenStream {
-        AttrTokenStream(Lrc::new(tokens))
+        AttrTokenStream(Arc::new(tokens))
     }
 
     /// Converts this `AttrTokenStream` to a plain `Vec<TokenTree>`. During
@@ -232,35 +233,52 @@ fn attrs_and_tokens_to_token_trees(
 
     // Insert inner attribute tokens.
     if !inner_attrs.is_empty() {
-        let mut found = false;
-        // Check the last two trees (to account for a trailing semi)
-        for tree in res.iter_mut().rev().take(2) {
-            if let TokenTree::Delimited(span, spacing, delim, delim_tokens) = tree {
-                // Inner attributes are only supported on extern blocks, functions,
-                // impls, and modules. All of these have their inner attributes
-                // placed at the beginning of the rightmost outermost braced group:
-                // e.g. fn foo() { #![my_attr] }
-                //
-                // Therefore, we can insert them back into the right location
-                // without needing to do any extra position tracking.
-                //
-                // Note: Outline modules are an exception - they can
-                // have attributes like `#![my_attr]` at the start of a file.
-                // Support for custom attributes in this position is not
-                // properly implemented - we always synthesize fake tokens,
-                // so we never reach this code.
+        let found = insert_inner_attrs(inner_attrs, res);
+        assert!(found, "Failed to find trailing delimited group in: {res:?}");
+    }
+
+    // Inner attributes are only supported on blocks, functions, impls, and
+    // modules. All of these have their inner attributes placed at the
+    // beginning of the rightmost outermost braced group:
+    // e.g. `fn foo() { #![my_attr] }`. (Note: the braces may be within
+    // invisible delimiters.)
+    //
+    // Therefore, we can insert them back into the right location without
+    // needing to do any extra position tracking.
+    //
+    // Note: Outline modules are an exception - they can have attributes like
+    // `#![my_attr]` at the start of a file. Support for custom attributes in
+    // this position is not properly implemented - we always synthesize fake
+    // tokens, so we never reach this code.
+    fn insert_inner_attrs(inner_attrs: &[Attribute], tts: &mut Vec<TokenTree>) -> bool {
+        for tree in tts.iter_mut().rev() {
+            if let TokenTree::Delimited(span, spacing, Delimiter::Brace, stream) = tree {
+                // Found it: the rightmost, outermost braced group.
                 let mut tts = vec![];
                 for inner_attr in inner_attrs {
                     tts.extend(inner_attr.token_trees());
                 }
-                tts.extend(delim_tokens.0.iter().cloned());
+                tts.extend(stream.0.iter().cloned());
                 let stream = TokenStream::new(tts);
-                *tree = TokenTree::Delimited(*span, *spacing, *delim, stream);
-                found = true;
-                break;
+                *tree = TokenTree::Delimited(*span, *spacing, Delimiter::Brace, stream);
+                return true;
+            } else if let TokenTree::Delimited(span, spacing, Delimiter::Invisible(src), stream) =
+                tree
+            {
+                // Recurse inside invisible delimiters.
+                let mut vec: Vec<_> = stream.iter().cloned().collect();
+                if insert_inner_attrs(inner_attrs, &mut vec) {
+                    *tree = TokenTree::Delimited(
+                        *span,
+                        *spacing,
+                        Delimiter::Invisible(*src),
+                        TokenStream::new(vec),
+                    );
+                    return true;
+                }
             }
         }
-        assert!(found, "Failed to find trailing delimited group in: {res:?}");
+        false
     }
 }
 
@@ -287,13 +305,8 @@ pub struct AttrsTarget {
 }
 
 /// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s.
-///
-/// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s
-/// instead of a representation of the abstract syntax tree.
-/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for
-/// backwards compatibility.
 #[derive(Clone, Debug, Default, Encodable, Decodable)]
-pub struct TokenStream(pub(crate) Lrc<Vec<TokenTree>>);
+pub struct TokenStream(pub(crate) Arc<Vec<TokenTree>>);
 
 /// Indicates whether a token can join with the following token to form a
 /// compound token. Used for conversions to `proc_macro::Spacing`. Also used to
@@ -412,7 +425,7 @@ impl PartialEq<TokenStream> for TokenStream {
 
 impl TokenStream {
     pub fn new(tts: Vec<TokenTree>) -> TokenStream {
-        TokenStream(Lrc::new(tts))
+        TokenStream(Arc::new(tts))
     }
 
     pub fn is_empty(&self) -> bool {
@@ -458,73 +471,6 @@ impl TokenStream {
         TokenStream::new(tts)
     }
 
-    pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream {
-        match nt {
-            Nonterminal::NtItem(item) => TokenStream::from_ast(item),
-            Nonterminal::NtBlock(block) => TokenStream::from_ast(block),
-            Nonterminal::NtStmt(stmt) if let StmtKind::Empty = stmt.kind => {
-                // FIXME: Properly collect tokens for empty statements.
-                TokenStream::token_alone(token::Semi, stmt.span)
-            }
-            Nonterminal::NtStmt(stmt) => TokenStream::from_ast(stmt),
-            Nonterminal::NtPat(pat) => TokenStream::from_ast(pat),
-            Nonterminal::NtTy(ty) => TokenStream::from_ast(ty),
-            Nonterminal::NtMeta(attr) => TokenStream::from_ast(attr),
-            Nonterminal::NtPath(path) => TokenStream::from_ast(path),
-            Nonterminal::NtVis(vis) => TokenStream::from_ast(vis),
-            Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => TokenStream::from_ast(expr),
-        }
-    }
-
-    fn flatten_token(token: &Token, spacing: Spacing) -> TokenTree {
-        match token.kind {
-            token::NtIdent(ident, is_raw) => {
-                TokenTree::Token(Token::new(token::Ident(ident.name, is_raw), ident.span), spacing)
-            }
-            token::NtLifetime(ident, is_raw) => TokenTree::Delimited(
-                DelimSpan::from_single(token.span),
-                DelimSpacing::new(Spacing::JointHidden, spacing),
-                Delimiter::Invisible(InvisibleOrigin::FlattenToken),
-                TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span),
-            ),
-            token::Interpolated(ref nt) => TokenTree::Delimited(
-                DelimSpan::from_single(token.span),
-                DelimSpacing::new(Spacing::JointHidden, spacing),
-                Delimiter::Invisible(InvisibleOrigin::FlattenToken),
-                TokenStream::from_nonterminal_ast(&nt).flattened(),
-            ),
-            _ => TokenTree::Token(token.clone(), spacing),
-        }
-    }
-
-    fn flatten_token_tree(tree: &TokenTree) -> TokenTree {
-        match tree {
-            TokenTree::Token(token, spacing) => TokenStream::flatten_token(token, *spacing),
-            TokenTree::Delimited(span, spacing, delim, tts) => {
-                TokenTree::Delimited(*span, *spacing, *delim, tts.flattened())
-            }
-        }
-    }
-
-    #[must_use]
-    pub fn flattened(&self) -> TokenStream {
-        fn can_skip(stream: &TokenStream) -> bool {
-            stream.iter().all(|tree| match tree {
-                TokenTree::Token(token, _) => !matches!(
-                    token.kind,
-                    token::NtIdent(..) | token::NtLifetime(..) | token::Interpolated(..)
-                ),
-                TokenTree::Delimited(.., inner) => can_skip(inner),
-            })
-        }
-
-        if can_skip(self) {
-            return self.clone();
-        }
-
-        self.iter().map(|tree| TokenStream::flatten_token_tree(tree)).collect()
-    }
-
     // If `vec` is not empty, try to glue `tt` onto its last token. The return
     // value indicates if gluing took place.
     fn try_glue_to_last(vec: &mut Vec<TokenTree>, tt: &TokenTree) -> bool {
@@ -544,7 +490,7 @@ impl TokenStream {
     /// Push `tt` onto the end of the stream, possibly gluing it to the last
     /// token. Uses `make_mut` to maximize efficiency.
     pub fn push_tree(&mut self, tt: TokenTree) {
-        let vec_mut = Lrc::make_mut(&mut self.0);
+        let vec_mut = Arc::make_mut(&mut self.0);
 
         if Self::try_glue_to_last(vec_mut, &tt) {
             // nothing else to do
@@ -557,7 +503,7 @@ impl TokenStream {
     /// token tree to the last token. (No other token trees will be glued.)
     /// Uses `make_mut` to maximize efficiency.
     pub fn push_stream(&mut self, stream: TokenStream) {
-        let vec_mut = Lrc::make_mut(&mut self.0);
+        let vec_mut = Arc::make_mut(&mut self.0);
 
         let stream_iter = stream.0.iter().cloned();
 
@@ -577,7 +523,7 @@ impl TokenStream {
     }
 
     /// Desugar doc comments like `/// foo` in the stream into `#[doc =
-    /// r"foo"]`. Modifies the `TokenStream` via `Lrc::make_mut`, but as little
+    /// r"foo"]`. Modifies the `TokenStream` via `Arc::make_mut`, but as little
     /// as possible.
     pub fn desugar_doc_comments(&mut self) {
         if let Some(desugared_stream) = desugar_inner(self.clone()) {
@@ -596,7 +542,7 @@ impl TokenStream {
                     ) => {
                         let desugared = desugared_tts(attr_style, data, span);
                         let desugared_len = desugared.len();
-                        Lrc::make_mut(&mut stream.0).splice(i..i + 1, desugared);
+                        Arc::make_mut(&mut stream.0).splice(i..i + 1, desugared);
                         modified = true;
                         i += desugared_len;
                     }
@@ -607,7 +553,7 @@ impl TokenStream {
                         if let Some(desugared_delim_stream) = desugar_inner(delim_stream.clone()) {
                             let new_tt =
                                 TokenTree::Delimited(sp, spacing, delim, desugared_delim_stream);
-                            Lrc::make_mut(&mut stream.0)[i] = new_tt;
+                            Arc::make_mut(&mut stream.0)[i] = new_tt;
                             modified = true;
                         }
                         i += 1;
@@ -655,7 +601,7 @@ impl TokenStream {
             if attr_style == AttrStyle::Inner {
                 vec![
                     TokenTree::token_joint(token::Pound, span),
-                    TokenTree::token_joint_hidden(token::Not, span),
+                    TokenTree::token_joint_hidden(token::Bang, span),
                     body,
                 ]
             } else {
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 64f2a98b8a6f..989ebe14bf8f 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -108,6 +108,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
             Assign(e, _, _)
             | AssignOp(_, e, _)
             | Await(e, _)
+            | Use(e, _)
             | Binary(_, e, _)
             | Call(e, _)
             | Cast(e, _)
@@ -181,11 +182,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
             | Range(_, Some(e), _)
             | Ret(Some(e))
             | Unary(_, e)
-            | Yield(Some(e))
             | Yeet(Some(e))
             | Become(e) => {
                 expr = e;
             }
+            Yield(kind) => match kind.expr() {
+                Some(e) => expr = e,
+                None => break None,
+            },
             Closure(closure) => {
                 expr = &closure.body;
             }
@@ -216,7 +220,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
             Break(_, None)
             | Range(_, None, _)
             | Ret(None)
-            | Yield(None)
             | Array(_)
             | Call(_, _)
             | MethodCall(_)
@@ -224,6 +227,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
             | Lit(_)
             | Type(_, _)
             | Await(_, _)
+            | Use(_, _)
             | Field(_, _)
             | Index(_, _, _)
             | Underscore
@@ -235,7 +239,9 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
             | Yeet(None)
             | UnsafeBinderCast(..)
             | Err(_)
-            | Dummy => break None,
+            | Dummy => {
+                break None;
+            }
         }
     }
 }
diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs
index f39142f08ba5..e12818623d8e 100644
--- a/compiler/rustc_ast/src/util/comments.rs
+++ b/compiler/rustc_ast/src/util/comments.rs
@@ -39,7 +39,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
         let mut i = 0;
         let mut j = lines.len();
         // first line of all-stars should be omitted
-        if !lines.is_empty() && lines[0].chars().all(|c| c == '*') {
+        if lines.first().is_some_and(|line| line.chars().all(|c| c == '*')) {
             i += 1;
         }
 
@@ -97,7 +97,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
                 return None;
             }
         }
-        if lines.is_empty() { None } else { Some(lines[0][..i].into()) }
+        Some(lines.first()?[..i].to_string())
     }
 
     let data_s = data.as_str();
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index 4459cb962e8e..b8526cf9d952 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -2,7 +2,7 @@
 
 use std::{ascii, fmt, str};
 
-use rustc_lexer::unescape::{
+use rustc_literal_escaper::{
     MixedUnit, Mode, byte_from_char, unescape_byte, unescape_char, unescape_mixed, unescape_unicode,
 };
 use rustc_span::{Span, Symbol, kw, sym};
@@ -121,7 +121,7 @@ impl LitKind {
             }
             token::ByteStrRaw(n) => {
                 // Raw strings have no escapes so we can convert the symbol
-                // directly to a `Lrc<u8>`.
+                // directly to a `Arc<u8>`.
                 let buf = symbol.as_str().to_owned().into_bytes();
                 LitKind::ByteStr(buf.into(), StrStyle::Raw(n))
             }
@@ -142,7 +142,7 @@ impl LitKind {
             }
             token::CStrRaw(n) => {
                 // Raw strings have no escapes so we can convert the symbol
-                // directly to a `Lrc<u8>` after appending the terminating NUL
+                // directly to a `Arc<u8>` after appending the terminating NUL
                 // char.
                 let mut buf = symbol.as_str().to_owned().into_bytes();
                 buf.push(0);
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 8f2b7a23c01c..1e5f414fae1c 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -1,59 +1,21 @@
 use rustc_span::kw;
 
-use crate::ast::{self, BinOpKind};
-use crate::token::{self, BinOpToken, Token};
+use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits};
+use crate::token::{self, Token};
 
-/// Associative operator with precedence.
-///
-/// This is the enum which specifies operator precedence and fixity to the parser.
+/// Associative operator.
 #[derive(Copy, Clone, PartialEq, Debug)]
 pub enum AssocOp {
-    /// `+`
-    Add,
-    /// `-`
-    Subtract,
-    /// `*`
-    Multiply,
-    /// `/`
-    Divide,
-    /// `%`
-    Modulus,
-    /// `&&`
-    LAnd,
-    /// `||`
-    LOr,
-    /// `^`
-    BitXor,
-    /// `&`
-    BitAnd,
-    /// `|`
-    BitOr,
-    /// `<<`
-    ShiftLeft,
-    /// `>>`
-    ShiftRight,
-    /// `==`
-    Equal,
-    /// `<`
-    Less,
-    /// `<=`
-    LessEqual,
-    /// `!=`
-    NotEqual,
-    /// `>`
-    Greater,
-    /// `>=`
-    GreaterEqual,
+    /// A binary op.
+    Binary(BinOpKind),
+    /// `?=` where ? is one of the assignable BinOps
+    AssignOp(AssignOpKind),
     /// `=`
     Assign,
-    /// `?=` where ? is one of the BinOpToken
-    AssignOp(BinOpToken),
     /// `as`
-    As,
-    /// `..` range
-    DotDot,
-    /// `..=` range
-    DotDotEq,
+    Cast,
+    /// `..` or `..=` range
+    Range(RangeLimits),
 }
 
 #[derive(PartialEq, Debug)]
@@ -67,81 +29,56 @@ pub enum Fixity {
 }
 
 impl AssocOp {
-    /// Creates a new AssocOP from a token
+    /// Creates a new AssocOp from a token.
     pub fn from_token(t: &Token) -> Option<AssocOp> {
         use AssocOp::*;
         match t.kind {
-            token::BinOpEq(k) => Some(AssignOp(k)),
             token::Eq => Some(Assign),
-            token::BinOp(BinOpToken::Star) => Some(Multiply),
-            token::BinOp(BinOpToken::Slash) => Some(Divide),
-            token::BinOp(BinOpToken::Percent) => Some(Modulus),
-            token::BinOp(BinOpToken::Plus) => Some(Add),
-            token::BinOp(BinOpToken::Minus) => Some(Subtract),
-            token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
-            token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
-            token::BinOp(BinOpToken::And) => Some(BitAnd),
-            token::BinOp(BinOpToken::Caret) => Some(BitXor),
-            token::BinOp(BinOpToken::Or) => Some(BitOr),
-            token::Lt => Some(Less),
-            token::Le => Some(LessEqual),
-            token::Ge => Some(GreaterEqual),
-            token::Gt => Some(Greater),
-            token::EqEq => Some(Equal),
-            token::Ne => Some(NotEqual),
-            token::AndAnd => Some(LAnd),
-            token::OrOr => Some(LOr),
-            token::DotDot => Some(DotDot),
-            token::DotDotEq => Some(DotDotEq),
+            token::Plus => Some(Binary(BinOpKind::Add)),
+            token::Minus => Some(Binary(BinOpKind::Sub)),
+            token::Star => Some(Binary(BinOpKind::Mul)),
+            token::Slash => Some(Binary(BinOpKind::Div)),
+            token::Percent => Some(Binary(BinOpKind::Rem)),
+            token::Caret => Some(Binary(BinOpKind::BitXor)),
+            token::And => Some(Binary(BinOpKind::BitAnd)),
+            token::Or => Some(Binary(BinOpKind::BitOr)),
+            token::Shl => Some(Binary(BinOpKind::Shl)),
+            token::Shr => Some(Binary(BinOpKind::Shr)),
+            token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)),
+            token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)),
+            token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)),
+            token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)),
+            token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)),
+            token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)),
+            token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)),
+            token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)),
+            token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)),
+            token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)),
+            token::Lt => Some(Binary(BinOpKind::Lt)),
+            token::Le => Some(Binary(BinOpKind::Le)),
+            token::Ge => Some(Binary(BinOpKind::Ge)),
+            token::Gt => Some(Binary(BinOpKind::Gt)),
+            token::EqEq => Some(Binary(BinOpKind::Eq)),
+            token::Ne => Some(Binary(BinOpKind::Ne)),
+            token::AndAnd => Some(Binary(BinOpKind::And)),
+            token::OrOr => Some(Binary(BinOpKind::Or)),
+            token::DotDot => Some(Range(RangeLimits::HalfOpen)),
             // DotDotDot is no longer supported, but we need some way to display the error
-            token::DotDotDot => Some(DotDotEq),
+            token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)),
             // `<-` should probably be `< -`
-            token::LArrow => Some(Less),
-            _ if t.is_keyword(kw::As) => Some(As),
+            token::LArrow => Some(Binary(BinOpKind::Lt)),
+            _ if t.is_keyword(kw::As) => Some(Cast),
             _ => None,
         }
     }
 
-    /// Creates a new AssocOp from ast::BinOpKind.
-    pub fn from_ast_binop(op: BinOpKind) -> Self {
-        use AssocOp::*;
-        match op {
-            BinOpKind::Lt => Less,
-            BinOpKind::Gt => Greater,
-            BinOpKind::Le => LessEqual,
-            BinOpKind::Ge => GreaterEqual,
-            BinOpKind::Eq => Equal,
-            BinOpKind::Ne => NotEqual,
-            BinOpKind::Mul => Multiply,
-            BinOpKind::Div => Divide,
-            BinOpKind::Rem => Modulus,
-            BinOpKind::Add => Add,
-            BinOpKind::Sub => Subtract,
-            BinOpKind::Shl => ShiftLeft,
-            BinOpKind::Shr => ShiftRight,
-            BinOpKind::BitAnd => BitAnd,
-            BinOpKind::BitXor => BitXor,
-            BinOpKind::BitOr => BitOr,
-            BinOpKind::And => LAnd,
-            BinOpKind::Or => LOr,
-        }
-    }
-
     /// Gets the precedence of this operator
     pub fn precedence(&self) -> ExprPrecedence {
         use AssocOp::*;
         match *self {
-            As => ExprPrecedence::Cast,
-            Multiply | Divide | Modulus => ExprPrecedence::Product,
-            Add | Subtract => ExprPrecedence::Sum,
-            ShiftLeft | ShiftRight => ExprPrecedence::Shift,
-            BitAnd => ExprPrecedence::BitAnd,
-            BitXor => ExprPrecedence::BitXor,
-            BitOr => ExprPrecedence::BitOr,
-            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
-            LAnd => ExprPrecedence::LAnd,
-            LOr => ExprPrecedence::LOr,
-            DotDot | DotDotEq => ExprPrecedence::Range,
+            Cast => ExprPrecedence::Cast,
+            Binary(bin_op) => bin_op.precedence(),
+            Range(_) => ExprPrecedence::Range,
             Assign | AssignOp(_) => ExprPrecedence::Assign,
         }
     }
@@ -152,22 +89,17 @@ impl AssocOp {
         // NOTE: it is a bug to have an operators that has same precedence but different fixities!
         match *self {
             Assign | AssignOp(_) => Fixity::Right,
-            As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
-            | BitXor | BitOr | LAnd | LOr => Fixity::Left,
-            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
-                Fixity::None
-            }
+            Binary(binop) => binop.fixity(),
+            Cast => Fixity::Left,
+            Range(_) => Fixity::None,
         }
     }
 
     pub fn is_comparison(&self) -> bool {
         use AssocOp::*;
         match *self {
-            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
-            Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
-            | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => {
-                false
-            }
+            Binary(binop) => binop.is_comparison(),
+            Assign | AssignOp(_) | Cast | Range(_) => false,
         }
     }
 
@@ -175,34 +107,7 @@ impl AssocOp {
         use AssocOp::*;
         match *self {
             Assign | AssignOp(_) => true,
-            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
-            | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
-            | BitOr | LAnd | LOr | DotDot | DotDotEq => false,
-        }
-    }
-
-    pub fn to_ast_binop(&self) -> Option<BinOpKind> {
-        use AssocOp::*;
-        match *self {
-            Less => Some(BinOpKind::Lt),
-            Greater => Some(BinOpKind::Gt),
-            LessEqual => Some(BinOpKind::Le),
-            GreaterEqual => Some(BinOpKind::Ge),
-            Equal => Some(BinOpKind::Eq),
-            NotEqual => Some(BinOpKind::Ne),
-            Multiply => Some(BinOpKind::Mul),
-            Divide => Some(BinOpKind::Div),
-            Modulus => Some(BinOpKind::Rem),
-            Add => Some(BinOpKind::Add),
-            Subtract => Some(BinOpKind::Sub),
-            ShiftLeft => Some(BinOpKind::Shl),
-            ShiftRight => Some(BinOpKind::Shr),
-            BitAnd => Some(BinOpKind::BitAnd),
-            BitXor => Some(BinOpKind::BitXor),
-            BitOr => Some(BinOpKind::BitOr),
-            LAnd => Some(BinOpKind::And),
-            LOr => Some(BinOpKind::Or),
-            Assign | AssignOp(_) | As | DotDot | DotDotEq => None,
+            Cast | Binary(_) | Range(_) => false,
         }
     }
 
@@ -212,20 +117,23 @@ impl AssocOp {
     /// parentheses while having a high degree of confidence on the correctness of the suggestion.
     pub fn can_continue_expr_unambiguously(&self) -> bool {
         use AssocOp::*;
+        use BinOpKind::*;
         matches!(
             self,
-            BitXor | // `{ 42 } ^ 3`
             Assign | // `{ 42 } = { 42 }`
-            Divide | // `{ 42 } / 42`
-            Modulus | // `{ 42 } % 2`
-            ShiftRight | // `{ 42 } >> 2`
-            LessEqual | // `{ 42 } <= 3`
-            Greater | // `{ 42 } > 3`
-            GreaterEqual | // `{ 42 } >= 3`
+            Binary(
+                BitXor | // `{ 42 } ^ 3`
+                Div | // `{ 42 } / 42`
+                Rem | // `{ 42 } % 2`
+                Shr | // `{ 42 } >> 2`
+                Le | // `{ 42 } <= 3`
+                Gt | // `{ 42 } > 3`
+                Ge   // `{ 42 } >= 3`
+            ) |
             AssignOp(_) | // `{ 42 } +=`
             // Equal | // `{ 42 } == { 42 }`    Accepting these here would regress incorrect
             // NotEqual | // `{ 42 } != { 42 }  struct literals parser recovery.
-            As // `{ 42 } as usize`
+            Cast // `{ 42 } as usize`
         )
     }
 }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 1d6d7330757d..79193fcec63a 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -16,6 +16,7 @@
 pub use rustc_ast_ir::visit::VisitorResult;
 pub use rustc_ast_ir::{try_visit, visit_opt, walk_list, walk_visitable_list};
 use rustc_span::{Ident, Span};
+use thin_vec::ThinVec;
 
 use crate::ast::*;
 use crate::ptr::P;
@@ -23,7 +24,7 @@ use crate::ptr::P;
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum AssocCtxt {
     Trait,
-    Impl,
+    Impl { of_trait: bool },
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -65,7 +66,7 @@ impl BoundKind {
 #[derive(Copy, Clone, Debug)]
 pub enum FnKind<'a> {
     /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
-    Fn(FnCtxt, &'a Ident, &'a FnSig, &'a Visibility, &'a Generics, &'a Option<P<Block>>),
+    Fn(FnCtxt, &'a Visibility, &'a Fn),
 
     /// E.g., `|x, y| body`.
     Closure(&'a ClosureBinder, &'a Option<CoroutineKind>, &'a FnDecl, &'a Expr),
@@ -74,21 +75,21 @@ pub enum FnKind<'a> {
 impl<'a> FnKind<'a> {
     pub fn header(&self) -> Option<&'a FnHeader> {
         match *self {
-            FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header),
+            FnKind::Fn(_, _, Fn { sig, .. }) => Some(&sig.header),
             FnKind::Closure(..) => None,
         }
     }
 
     pub fn ident(&self) -> Option<&Ident> {
         match self {
-            FnKind::Fn(_, ident, ..) => Some(ident),
+            FnKind::Fn(_, _, Fn { ident, .. }) => Some(ident),
             _ => None,
         }
     }
 
     pub fn decl(&self) -> &'a FnDecl {
         match self {
-            FnKind::Fn(_, _, sig, _, _, _) => &sig.decl,
+            FnKind::Fn(_, _, Fn { sig, .. }) => &sig.decl,
             FnKind::Closure(_, _, decl, _) => decl,
         }
     }
@@ -117,7 +118,6 @@ pub trait WalkItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         visibility: &'a Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut V,
@@ -179,6 +179,9 @@ pub trait Visitor<'ast>: Sized {
     fn visit_ty(&mut self, t: &'ast Ty) -> Self::Result {
         walk_ty(self, t)
     }
+    fn visit_ty_pat(&mut self, t: &'ast TyPat) -> Self::Result {
+        walk_ty_pat(self, t)
+    }
     fn visit_generic_param(&mut self, param: &'ast GenericParam) -> Self::Result {
         walk_generic_param(self, param)
     }
@@ -188,6 +191,9 @@ pub trait Visitor<'ast>: Sized {
     fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result {
         walk_closure_binder(self, b)
     }
+    fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result {
+        walk_contract(self, c)
+    }
     fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
         walk_where_predicate(self, p)
     }
@@ -317,7 +323,7 @@ pub fn walk_crate<'a, V: Visitor<'a>>(visitor: &mut V, krate: &'a Crate) -> V::R
 }
 
 pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) -> V::Result {
-    let Local { id: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
+    let Local { id: _, super_: _, pat, ty, kind, span: _, colon_sp: _, attrs, tokens: _ } = local;
     walk_list!(visitor, visit_attribute, attrs);
     try_visit!(visitor.visit_pat(pat));
     visit_opt!(visitor, visit_ty, ty);
@@ -357,49 +363,72 @@ impl WalkItemKind for ItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         vis: &'a Visibility,
         _ctxt: Self::Ctxt,
         visitor: &mut V,
     ) -> V::Result {
         match self {
-            ItemKind::ExternCrate(_rename) => {}
+            ItemKind::ExternCrate(_rename, ident) => try_visit!(visitor.visit_ident(ident)),
             ItemKind::Use(use_tree) => try_visit!(visitor.visit_use_tree(use_tree, id, false)),
-            ItemKind::Static(box StaticItem { ty, safety: _, mutability: _, expr }) => {
+            ItemKind::Static(box StaticItem {
+                ident,
+                ty,
+                safety: _,
+                mutability: _,
+                expr,
+                define_opaque,
+            }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
+                try_visit!(walk_define_opaques(visitor, define_opaque));
             }
-            ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
+            ItemKind::Const(box ConstItem {
+                defaultness: _,
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+            }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
+                try_visit!(walk_define_opaques(visitor, define_opaque));
             }
-            ItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
-                let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, body);
+            ItemKind::Fn(func) => {
+                let kind = FnKind::Fn(FnCtxt::Free, vis, &*func);
                 try_visit!(visitor.visit_fn(kind, span, id));
             }
-            ItemKind::Mod(_unsafety, mod_kind) => match mod_kind {
-                ModKind::Loaded(items, _inline, _inner_span, _) => {
-                    walk_list!(visitor, visit_item, items);
+            ItemKind::Mod(_unsafety, ident, mod_kind) => {
+                try_visit!(visitor.visit_ident(ident));
+                match mod_kind {
+                    ModKind::Loaded(items, _inline, _inner_span, _) => {
+                        walk_list!(visitor, visit_item, items);
+                    }
+                    ModKind::Unloaded => {}
                 }
-                ModKind::Unloaded => {}
-            },
+            }
             ItemKind::ForeignMod(ForeignMod { extern_span: _, safety: _, abi: _, items }) => {
                 walk_list!(visitor, visit_foreign_item, items);
             }
             ItemKind::GlobalAsm(asm) => try_visit!(visitor.visit_inline_asm(asm)),
             ItemKind::TyAlias(box TyAlias {
                 generics,
+                ident,
                 bounds,
                 ty,
                 defaultness: _,
                 where_clauses: _,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
                 visit_opt!(visitor, visit_ty, ty);
             }
-            ItemKind::Enum(enum_definition, generics) => {
+            ItemKind::Enum(ident, enum_definition, generics) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_enum_def(enum_definition));
             }
@@ -416,34 +445,54 @@ impl WalkItemKind for ItemKind {
                 try_visit!(visitor.visit_generics(generics));
                 visit_opt!(visitor, visit_trait_ref, of_trait);
                 try_visit!(visitor.visit_ty(self_ty));
-                walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Impl);
+                walk_list!(
+                    visitor,
+                    visit_assoc_item,
+                    items,
+                    AssocCtxt::Impl { of_trait: of_trait.is_some() }
+                );
             }
-            ItemKind::Struct(struct_definition, generics)
-            | ItemKind::Union(struct_definition, generics) => {
+            ItemKind::Struct(ident, struct_definition, generics)
+            | ItemKind::Union(ident, struct_definition, generics) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_variant_data(struct_definition));
             }
-            ItemKind::Trait(box Trait { safety: _, is_auto: _, generics, bounds, items }) => {
+            ItemKind::Trait(box Trait {
+                safety: _,
+                is_auto: _,
+                ident,
+                generics,
+                bounds,
+                items,
+            }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits);
                 walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait);
             }
-            ItemKind::TraitAlias(generics, bounds) => {
+            ItemKind::TraitAlias(ident, generics, bounds) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
             }
             ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
-            ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, id)),
+            ItemKind::MacroDef(ident, ts) => {
+                try_visit!(visitor.visit_ident(ident));
+                try_visit!(visitor.visit_mac_def(ts, id))
+            }
             ItemKind::Delegation(box Delegation {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
             }) => {
                 try_visit!(visitor.visit_qself(qself));
                 try_visit!(visitor.visit_path(path, *id));
+                try_visit!(visitor.visit_ident(ident));
                 visit_opt!(visitor, visit_ident, rename);
                 visit_opt!(visitor, visit_block, body);
             }
@@ -531,7 +580,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
         }
         TyKind::Pat(ty, pat) => {
             try_visit!(visitor.visit_ty(ty));
-            try_visit!(visitor.visit_pat(pat));
+            try_visit!(visitor.visit_ty_pat(pat));
         }
         TyKind::Array(ty, length) => {
             try_visit!(visitor.visit_ty(ty));
@@ -552,6 +601,18 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
     V::Result::output()
 }
 
+pub fn walk_ty_pat<'a, V: Visitor<'a>>(visitor: &mut V, tp: &'a TyPat) -> V::Result {
+    let TyPat { id: _, kind, span: _, tokens: _ } = tp;
+    match kind {
+        TyPatKind::Range(start, end, _include_end) => {
+            visit_opt!(visitor, visit_anon_const, start);
+            visit_opt!(visitor, visit_anon_const, end);
+        }
+        TyPatKind::Err(_) => {}
+    }
+    V::Result::output()
+}
+
 fn walk_qself<'a, V: Visitor<'a>>(visitor: &mut V, qself: &'a Option<P<QSelf>>) -> V::Result {
     if let Some(qself) = qself {
         let QSelf { ty, path_span: _, position: _ } = &**qself;
@@ -579,7 +640,7 @@ pub fn walk_use_tree<'a, V: Visitor<'a>>(
             visit_opt!(visitor, visit_ident, rename);
         }
         UseTreeKind::Glob => {}
-        UseTreeKind::Nested { ref items, span: _ } => {
+        UseTreeKind::Nested { items, span: _ } => {
             for &(ref nested_tree, nested_id) in items {
                 try_visit!(visitor.visit_use_tree(nested_tree, nested_id, true));
             }
@@ -689,7 +750,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
             try_visit!(visitor.visit_pat(subpattern));
             try_visit!(visitor.visit_expr(guard_condition));
         }
-        PatKind::Wild | PatKind::Rest | PatKind::Never => {}
+        PatKind::Missing | PatKind::Wild | PatKind::Rest | PatKind::Never => {}
         PatKind::Err(_guar) => {}
         PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
             walk_list!(visitor, visit_pat, elems);
@@ -705,27 +766,37 @@ impl WalkItemKind for ForeignItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         vis: &'a Visibility,
         _ctxt: Self::Ctxt,
         visitor: &mut V,
     ) -> V::Result {
         match self {
-            ForeignItemKind::Static(box StaticItem { ty, mutability: _, expr, safety: _ }) => {
+            ForeignItemKind::Static(box StaticItem {
+                ident,
+                ty,
+                mutability: _,
+                expr,
+                safety: _,
+                define_opaque,
+            }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
+                try_visit!(walk_define_opaques(visitor, define_opaque));
             }
-            ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
-                let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body);
+            ForeignItemKind::Fn(func) => {
+                let kind = FnKind::Fn(FnCtxt::Foreign, vis, &*func);
                 try_visit!(visitor.visit_fn(kind, span, id));
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 generics,
+                ident,
                 bounds,
                 ty,
                 defaultness: _,
                 where_clauses: _,
             }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
                 visit_opt!(visitor, visit_ty, ty);
@@ -800,11 +871,23 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>(
     V::Result::output()
 }
 
+pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result {
+    let FnContract { requires, ensures } = c;
+    if let Some(pred) = requires {
+        visitor.visit_expr(pred);
+    }
+    if let Some(pred) = ensures {
+        visitor.visit_expr(pred);
+    }
+    V::Result::output()
+}
+
 pub fn walk_where_predicate<'a, V: Visitor<'a>>(
     visitor: &mut V,
     predicate: &'a WherePredicate,
 ) -> V::Result {
-    let WherePredicate { kind, id: _, span: _ } = predicate;
+    let WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate;
+    walk_list!(visitor, visit_attribute, attrs);
     visitor.visit_where_predicate_kind(kind)
 }
 
@@ -858,12 +941,27 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>(
 
 pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Result {
     match kind {
-        FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) => {
-            // Identifier and visibility are visited as a part of the item.
+        FnKind::Fn(
+            _ctxt,
+            _vis,
+            Fn {
+                defaultness: _,
+                ident,
+                sig: FnSig { header, decl, span: _ },
+                generics,
+                contract,
+                body,
+                define_opaque,
+            },
+        ) => {
+            // Visibility is visited as a part of the item.
+            try_visit!(visitor.visit_ident(ident));
             try_visit!(visitor.visit_fn_header(header));
             try_visit!(visitor.visit_generics(generics));
             try_visit!(visitor.visit_fn_decl(decl));
+            visit_opt!(visitor, visit_contract, contract);
             visit_opt!(visitor, visit_block, body);
+            try_visit!(walk_define_opaques(visitor, define_opaque));
         }
         FnKind::Closure(binder, coroutine_kind, decl, body) => {
             try_visit!(visitor.visit_closure_binder(binder));
@@ -881,29 +979,39 @@ impl WalkItemKind for AssocItemKind {
         &'a self,
         span: Span,
         id: NodeId,
-        ident: &'a Ident,
         vis: &'a Visibility,
         ctxt: Self::Ctxt,
         visitor: &mut V,
     ) -> V::Result {
         match self {
-            AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
+            AssocItemKind::Const(box ConstItem {
+                defaultness: _,
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+            }) => {
+                try_visit!(visitor.visit_ident(ident));
                 try_visit!(visitor.visit_generics(generics));
                 try_visit!(visitor.visit_ty(ty));
                 visit_opt!(visitor, visit_expr, expr);
+                try_visit!(walk_define_opaques(visitor, define_opaque));
             }
-            AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
-                let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body);
+            AssocItemKind::Fn(func) => {
+                let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), vis, &*func);
                 try_visit!(visitor.visit_fn(kind, span, id));
             }
             AssocItemKind::Type(box TyAlias {
                 generics,
+                ident,
                 bounds,
                 ty,
                 defaultness: _,
                 where_clauses: _,
             }) => {
                 try_visit!(visitor.visit_generics(generics));
+                try_visit!(visitor.visit_ident(ident));
                 walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
                 visit_opt!(visitor, visit_ty, ty);
             }
@@ -914,12 +1022,14 @@ impl WalkItemKind for AssocItemKind {
                 id,
                 qself,
                 path,
+                ident,
                 rename,
                 body,
                 from_glob: _,
             }) => {
                 try_visit!(visitor.visit_qself(qself));
                 try_visit!(visitor.visit_path(path, *id));
+                try_visit!(visitor.visit_ident(ident));
                 visit_opt!(visitor, visit_ident, rename);
                 visit_opt!(visitor, visit_block, body);
             }
@@ -961,11 +1071,10 @@ fn walk_item_ctxt<'a, V: Visitor<'a>, K: WalkItemKind>(
     item: &'a Item<K>,
     ctxt: K::Ctxt,
 ) -> V::Result {
-    let Item { id, span, ident, vis, attrs, kind, tokens: _ } = item;
+    let Item { id, span, vis, attrs, kind, tokens: _ } = item;
     walk_list!(visitor, visit_attribute, attrs);
     try_visit!(visitor.visit_vis(vis));
-    try_visit!(visitor.visit_ident(ident));
-    try_visit!(kind.walk(*span, *id, ident, vis, ctxt, visitor));
+    try_visit!(kind.walk(*span, *id, vis, ctxt, visitor));
     V::Result::output()
 }
 
@@ -989,7 +1098,7 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef)
 }
 
 pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) -> V::Result {
-    let Block { stmts, id: _, rules: _, span: _, tokens: _, could_be_bare_literal: _ } = block;
+    let Block { stmts, id: _, rules: _, span: _, tokens: _ } = block;
     walk_list!(visitor, visit_stmt, stmts);
     V::Result::output()
 }
@@ -1167,7 +1276,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
                 FnKind::Closure(binder, coroutine_kind, fn_decl, body),
                 *span,
                 *id
-            ))
+            ));
         }
         ExprKind::Block(block, opt_label) => {
             visit_opt!(visitor, visit_label, opt_label);
@@ -1175,6 +1284,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
         }
         ExprKind::Gen(_capt, body, _kind, _decl_span) => try_visit!(visitor.visit_block(body)),
         ExprKind::Await(expr, _span) => try_visit!(visitor.visit_expr(expr)),
+        ExprKind::Use(expr, _span) => try_visit!(visitor.visit_expr(expr)),
         ExprKind::Assign(lhs, rhs, _span) => {
             try_visit!(visitor.visit_expr(lhs));
             try_visit!(visitor.visit_expr(rhs));
@@ -1222,8 +1332,8 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
             try_visit!(visitor.visit_ty(container));
             walk_list!(visitor, visit_ident, fields.iter());
         }
-        ExprKind::Yield(optional_expression) => {
-            visit_opt!(visitor, visit_expr, optional_expression);
+        ExprKind::Yield(kind) => {
+            visit_opt!(visitor, visit_expr, kind.expr());
         }
         ExprKind::Try(subexpression) => try_visit!(visitor.visit_expr(subexpression)),
         ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)),
@@ -1290,3 +1400,15 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) -
     }
     V::Result::output()
 }
+
+fn walk_define_opaques<'a, V: Visitor<'a>>(
+    visitor: &mut V,
+    define_opaque: &'a Option<ThinVec<(NodeId, Path)>>,
+) -> V::Result {
+    if let Some(define_opaque) = define_opaque {
+        for (id, path) in define_opaque {
+            try_visit!(visitor.visit_path(path, *id));
+        }
+    }
+    V::Result::output()
+}
diff --git a/compiler/rustc_ast_ir/Cargo.toml b/compiler/rustc_ast_ir/Cargo.toml
index 1905574073f1..76bdd9f7eb6d 100644
--- a/compiler/rustc_ast_ir/Cargo.toml
+++ b/compiler/rustc_ast_ir/Cargo.toml
@@ -1,14 +1,13 @@
 [package]
 name = "rustc_ast_ir"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_macros = { path = "../rustc_macros", optional = true }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
-rustc_span = { path = "../rustc_span", optional = true }
 # tidy-alphabetical-end
 
 [features]
@@ -17,5 +16,4 @@ nightly = [
     "dep:rustc_serialize",
     "dep:rustc_data_structures",
     "dep:rustc_macros",
-    "dep:rustc_span",
 ]
diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs
index ff9d940ce9f2..0898433a74c5 100644
--- a/compiler/rustc_ast_ir/src/lib.rs
+++ b/compiler/rustc_ast_ir/src/lib.rs
@@ -1,19 +1,28 @@
+//! Common utilities shared by both `rustc_ast` and `rustc_type_ir`.
+//!
+//! Don't depend on this crate directly; both of those crates should re-export
+//! the functionality. Additionally, if you're in scope of `rustc_middle`, then
+//! prefer imports via that too, to avoid needing to directly depend on (e.g.)
+//! `rustc_type_ir` for a single import.
+
 // tidy-alphabetical-start
 #![cfg_attr(feature = "nightly", allow(internal_features))]
 #![cfg_attr(feature = "nightly", feature(never_type))]
 #![cfg_attr(feature = "nightly", feature(rustc_attrs))]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 #[cfg(feature = "nightly")]
-use rustc_macros::{Decodable, Encodable, HashStable_NoContext};
+use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
 
 pub mod visit;
 
 /// The movability of a coroutine / closure literal:
 /// whether a coroutine contains self-references, causing it to be `!Unpin`.
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
 pub enum Movability {
     /// May contain self-references, `!Unpin`.
     Static,
@@ -22,7 +31,10 @@ pub enum Movability {
 }
 
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
 pub enum Mutability {
     // N.B. Order is deliberate, so that Not < Mut
     Not,
@@ -81,7 +93,10 @@ impl Mutability {
 }
 
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
 pub enum Pinnedness {
     Not,
     Pinned,
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml
index 8cc4521e0a78..6ac258155fe9 100644
--- a/compiler/rustc_ast_lowering/Cargo.toml
+++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -1,17 +1,20 @@
 [package]
 name = "rustc_ast_lowering"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [lib]
 doctest = false
 
 [dependencies]
 # tidy-alphabetical-start
+rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
+rustc_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index f96c9fe8e327..5ef76fb64aaf 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -37,11 +37,11 @@ ast_lowering_bad_return_type_notation_inputs =
     .suggestion = remove the input types
 
 ast_lowering_bad_return_type_notation_needs_dots = return type notation arguments must be elided with `..`
-    .suggestion = add `..`
+    .suggestion = use the correct syntax by adding `..` to the arguments
 
 ast_lowering_bad_return_type_notation_output =
     return type not allowed with return type notation
-    .suggestion = remove the return type
+ast_lowering_bad_return_type_notation_output_suggestion = use the right argument notation and remove the return type
 
 ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
 
@@ -88,7 +88,7 @@ ast_lowering_invalid_abi_clobber_abi =
     invalid ABI for `clobber_abi`
     .note = the following ABIs are supported on this target: {$supported_abis}
 
-ast_lowering_invalid_abi_suggestion = did you mean
+ast_lowering_invalid_abi_suggestion = there's a similarly named valid ABI `{$suggestion}`
 
 ast_lowering_invalid_asm_template_modifier_const =
     asm template modifiers are not allowed for `const` arguments
@@ -112,7 +112,8 @@ ast_lowering_invalid_register =
     invalid register `{$reg}`: {$error}
 
 ast_lowering_invalid_register_class =
-    invalid register class `{$reg_class}`: {$error}
+    invalid register class `{$reg_class}`: unknown register class
+    .note = the following register classes are supported on this target: {$supported_register_classes}
 
 ast_lowering_match_arm_with_no_body =
     `match` arm with no body
@@ -140,9 +141,6 @@ ast_lowering_never_pattern_with_guard =
 
 ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
 
-ast_lowering_no_precise_captures_on_rpitit = `use<...>` precise capturing syntax is currently not allowed in return-position `impl Trait` in traits
-    .note = currently, return-position `impl Trait` in traits and trait implementations capture all lifetimes in scope
-
 ast_lowering_previously_used_here = previously used here
 
 ast_lowering_register1 = register `{$reg1_name}`
@@ -184,8 +182,6 @@ ast_lowering_underscore_expr_lhs_assign =
 ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture
 ast_lowering_unstable_inline_assembly_label_operand_with_outputs =
     using both label and output operands for inline assembly is unstable
-ast_lowering_unstable_inline_assembly_label_operands =
-    label operands for inline assembly are unstable
 ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable
 
 ast_lowering_use_angle_brackets = use angle brackets instead
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 2f1f1269ece5..af279e07acc6 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -1,13 +1,12 @@
 use std::collections::hash_map::Entry;
 use std::fmt::Write;
 
-use rustc_ast::ptr::P;
 use rustc_ast::*;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_session::parse::feature_err;
-use rustc_span::{Span, kw, sym};
+use rustc_span::{Span, sym};
 use rustc_target::asm;
 
 use super::LoweringContext;
@@ -39,6 +38,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
         if let Some(asm_arch) = asm_arch {
             // Inline assembly is currently only stable for these architectures.
+            // (See also compiletest's `has_asm_support`.)
             let is_stable = matches!(
                 asm_arch,
                 asm::InlineAsmArch::X86
@@ -152,11 +152,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     InlineAsmRegOrRegClass::RegClass(reg_class) => {
                         asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
                             asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else(
-                                |error| {
+                                |supported_register_classes| {
+                                    let mut register_classes =
+                                        format!("`{}`", supported_register_classes[0]);
+                                    for m in &supported_register_classes[1..] {
+                                        let _ = write!(register_classes, ", `{m}`");
+                                    }
                                     self.dcx().emit_err(InvalidRegisterClass {
                                         op_span: *op_sp,
                                         reg_class,
-                                        error,
+                                        supported_register_classes: register_classes,
                                     });
                                     asm::InlineAsmRegClass::Err
                                 },
@@ -191,7 +196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         }
                     }
                     InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
-                        anon_const: self.lower_anon_const_to_anon_const(anon_const),
+                        anon_const: self.lower_const_block(anon_const),
                     },
                     InlineAsmOperand::Sym { sym } => {
                         let static_def_id = self
@@ -225,20 +230,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 tokens: None,
                             };
 
-                            // Wrap the expression in an AnonConst.
-                            let parent_def_id = self.current_hir_id_owner.def_id;
-                            let node_id = self.next_node_id();
-                            self.create_def(
-                                parent_def_id,
-                                node_id,
-                                kw::Empty,
-                                DefKind::AnonConst,
-                                *op_sp,
-                            );
-                            let anon_const = AnonConst { id: node_id, value: P(expr) };
-                            hir::InlineAsmOperand::SymFn {
-                                anon_const: self.lower_anon_const_to_anon_const(&anon_const),
-                            }
+                            hir::InlineAsmOperand::SymFn { expr: self.lower_expr(&expr) }
                         }
                     }
                     InlineAsmOperand::Label { block } => {
@@ -478,22 +470,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
         }
 
-        // Feature gate checking for asm goto.
+        // Feature gate checking for `asm_goto_with_outputs`.
         if let Some((_, op_sp)) =
             operands.iter().find(|(op, _)| matches!(op, hir::InlineAsmOperand::Label { .. }))
         {
-            if !self.tcx.features().asm_goto() {
-                feature_err(
-                    sess,
-                    sym::asm_goto,
-                    *op_sp,
-                    fluent::ast_lowering_unstable_inline_assembly_label_operands,
-                )
-                .emit();
-            }
-
-            // In addition, check if an output operand is used.
-            // This is gated behind an additional feature.
+            // Check if an output operand is used.
             let output_operand_used = operands.iter().any(|(op, _)| {
                 matches!(
                     op,
diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs
index 88ce6f80e10b..c3222b79e55c 100644
--- a/compiler/rustc_ast_lowering/src/block.rs
+++ b/compiler/rustc_ast_lowering/src/block.rs
@@ -95,6 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> {
         // Let statements are allowed to have impl trait in bindings.
+        let super_ = l.super_;
         let ty = l.ty.as_ref().map(|t| {
             self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable))
         });
@@ -108,8 +109,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         };
         let span = self.lower_span(l.span);
         let source = hir::LocalSource::Normal;
-        self.lower_attrs(hir_id, &l.attrs);
-        self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source })
+        self.lower_attrs(hir_id, &l.attrs, l.span);
+        self.arena.alloc(hir::LetStmt { hir_id, super_, ty, pat, init, els, span, source })
     }
 
     fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index f9fe4938ca8b..93c627f64c96 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -41,13 +41,13 @@ use std::iter;
 use ast::visit::Visitor;
 use hir::def::{DefKind, PartialRes, Res};
 use hir::{BodyId, HirId};
+use rustc_abi::ExternAbi;
 use rustc_ast::*;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::DefId;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{Asyncness, ResolverAstLowering};
-use rustc_span::{Ident, Span};
-use rustc_target::spec::abi;
+use rustc_span::{Ident, Span, Symbol};
 use {rustc_ast as ast, rustc_hir as hir};
 
 use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode};
@@ -56,29 +56,38 @@ use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt};
 pub(crate) struct DelegationResults<'hir> {
     pub body_id: hir::BodyId,
     pub sig: hir::FnSig<'hir>,
+    pub ident: Ident,
     pub generics: &'hir hir::Generics<'hir>,
 }
 
 impl<'hir> LoweringContext<'_, 'hir> {
-    pub(crate) fn delegation_has_self(&self, item_id: NodeId, path_id: NodeId, span: Span) -> bool {
-        let sig_id = self.get_delegation_sig_id(item_id, path_id, span);
+    /// Defines whether the delegatee is an associated function whose first parameter is `self`.
+    pub(crate) fn delegatee_is_method(
+        &self,
+        item_id: NodeId,
+        path_id: NodeId,
+        span: Span,
+        is_in_trait_impl: bool,
+    ) -> bool {
+        let sig_id = self.get_delegation_sig_id(item_id, path_id, span, is_in_trait_impl);
         let Ok(sig_id) = sig_id else {
             return false;
         };
-        self.has_self(sig_id, span)
+        self.is_method(sig_id, span)
     }
 
-    fn has_self(&self, def_id: DefId, span: Span) -> bool {
-        if let Some(local_sig_id) = def_id.as_local() {
-            // The value may be missing due to recursive delegation.
-            // Error will be emitted later during HIR ty lowering.
-            self.resolver.delegation_fn_sigs.get(&local_sig_id).is_some_and(|sig| sig.has_self)
-        } else {
-            match self.tcx.def_kind(def_id) {
-                DefKind::Fn => false,
-                DefKind::AssocFn => self.tcx.associated_item(def_id).fn_has_self_parameter,
-                _ => span_bug!(span, "unexpected DefKind for delegation item"),
-            }
+    fn is_method(&self, def_id: DefId, span: Span) -> bool {
+        match self.tcx.def_kind(def_id) {
+            DefKind::Fn => false,
+            DefKind::AssocFn => match def_id.as_local() {
+                Some(local_def_id) => self
+                    .resolver
+                    .delegation_fn_sigs
+                    .get(&local_def_id)
+                    .is_some_and(|sig| sig.has_self),
+                None => self.tcx.associated_item(def_id).is_method(),
+            },
+            _ => span_bug!(span, "unexpected DefKind for delegation item"),
         }
     }
 
@@ -86,18 +95,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         delegation: &Delegation,
         item_id: NodeId,
+        is_in_trait_impl: bool,
     ) -> DelegationResults<'hir> {
         let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span);
-        let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span);
+        let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl);
         match sig_id {
             Ok(sig_id) => {
                 let (param_count, c_variadic) = self.param_count(sig_id);
                 let decl = self.lower_delegation_decl(sig_id, param_count, c_variadic, span);
                 let sig = self.lower_delegation_sig(sig_id, decl, span);
                 let body_id = self.lower_delegation_body(delegation, param_count, span);
-
+                let ident = self.lower_ident(delegation.ident);
                 let generics = self.lower_delegation_generics(span);
-                DelegationResults { body_id, sig, generics }
+                DelegationResults { body_id, sig, ident, generics }
             }
             Err(err) => self.generate_delegation_error(err, span),
         }
@@ -108,8 +118,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         item_id: NodeId,
         path_id: NodeId,
         span: Span,
+        is_in_trait_impl: bool,
     ) -> Result<DefId, ErrorGuaranteed> {
-        let sig_id = if self.is_in_trait_impl { item_id } else { path_id };
+        let sig_id = if is_in_trait_impl { item_id } else { path_id };
         self.get_resolution_id(sig_id, span)
     }
 
@@ -188,14 +199,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> hir::FnSig<'hir> {
         let header = if let Some(local_sig_id) = sig_id.as_local() {
             match self.resolver.delegation_fn_sigs.get(&local_sig_id) {
-                Some(sig) => self.lower_fn_header(
-                    sig.header,
+                Some(sig) => {
+                    let parent = self.tcx.parent(sig_id);
                     // HACK: we override the default safety instead of generating attributes from the ether.
                     // We are not forwarding the attributes, as the delegation fn sigs are collected on the ast,
                     // and here we need the hir attributes.
-                    if sig.target_feature { hir::Safety::Unsafe } else { hir::Safety::Safe },
-                    &[],
-                ),
+                    let default_safety =
+                        if sig.target_feature || self.tcx.def_kind(parent) == DefKind::ForeignMod {
+                            hir::Safety::Unsafe
+                        } else {
+                            hir::Safety::Safe
+                        };
+                    self.lower_fn_header(sig.header, default_safety, &[])
+                }
                 None => self.generate_header_error(),
             }
         } else {
@@ -218,12 +234,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
         hir::FnSig { decl, header, span }
     }
 
-    fn generate_param(&mut self, span: Span) -> (hir::Param<'hir>, NodeId) {
+    fn generate_param(&mut self, idx: usize, span: Span) -> (hir::Param<'hir>, NodeId) {
         let pat_node_id = self.next_node_id();
         let pat_id = self.lower_node_id(pat_node_id);
+        let ident = Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}")));
         let pat = self.arena.alloc(hir::Pat {
             hir_id: pat_id,
-            kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, Ident::empty(), None),
+            kind: hir::PatKind::Binding(hir::BindingMode::NONE, pat_id, ident, None),
             span,
             default_binding_modes: false,
         });
@@ -231,9 +248,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         (hir::Param { hir_id: self.next_id(), pat, ty_span: span, span }, pat_node_id)
     }
 
-    fn generate_arg(&mut self, param_id: HirId, span: Span) -> hir::Expr<'hir> {
+    fn generate_arg(&mut self, idx: usize, param_id: HirId, span: Span) -> hir::Expr<'hir> {
         let segments = self.arena.alloc_from_iter(iter::once(hir::PathSegment {
-            ident: Ident::empty(),
+            ident: Ident::with_dummy_span(Symbol::intern(&format!("arg{idx}"))),
             hir_id: self.next_id(),
             res: Res::Local(param_id),
             args: None,
@@ -257,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);
 
             for idx in 0..param_count {
-                let (param, pat_node_id) = this.generate_param(span);
+                let (param, pat_node_id) = this.generate_param(idx, span);
                 parameters.push(param);
 
                 let arg = if let Some(block) = block
@@ -273,7 +290,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     this.ident_and_label_to_local_id.insert(pat_node_id, param.pat.hir_id.local_id);
                     this.lower_target_expr(&block)
                 } else {
-                    this.generate_arg(param.pat.hir_id, span)
+                    this.generate_arg(idx, param.pat.hir_id, span)
                 };
                 args.push(arg);
             }
@@ -324,10 +341,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let call = if self
             .get_resolution_id(delegation.id, span)
-            .and_then(|def_id| Ok(self.has_self(def_id, span)))
+            .and_then(|def_id| Ok(self.is_method(def_id, span)))
             .unwrap_or_default()
             && delegation.qself.is_none()
             && !has_generic_args
+            && !args.is_empty()
         {
             let ast_segment = delegation.path.segments.last().unwrap();
             let segment = self.lower_path_segment(
@@ -389,8 +407,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let header = self.generate_header_error();
         let sig = hir::FnSig { decl, header, span };
 
+        let ident = Ident::dummy();
         let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span)));
-        DelegationResults { generics, body_id, sig }
+        DelegationResults { ident, generics, body_id, sig }
     }
 
     fn generate_header_error(&self) -> hir::FnHeader {
@@ -398,7 +417,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             safety: hir::Safety::Safe.into(),
             constness: hir::Constness::NotConst,
             asyncness: hir::IsAsync::NotAsync,
-            abi: abi::Abi::Rust,
+            abi: ExternAbi::Rust,
         }
     }
 
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index f727691bf479..576fa9731e90 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -1,5 +1,5 @@
+use rustc_errors::DiagArgFromDisplay;
 use rustc_errors::codes::*;
-use rustc_errors::{Diag, DiagArgFromDisplay, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{Ident, Span, Symbol};
 
@@ -32,8 +32,6 @@ pub(crate) struct InvalidAbi {
     pub abi: Symbol,
     pub command: String,
     #[subdiagnostic]
-    pub explain: Option<InvalidAbiReason>,
-    #[subdiagnostic]
     pub suggestion: Option<InvalidAbiSuggestion>,
 }
 
@@ -45,24 +43,12 @@ pub(crate) struct TupleStructWithDefault {
     pub span: Span,
 }
 
-pub(crate) struct InvalidAbiReason(pub &'static str);
-
-impl Subdiagnostic for InvalidAbiReason {
-    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
-        self,
-        diag: &mut Diag<'_, G>,
-        _: &F,
-    ) {
-        #[allow(rustc::untranslatable_diagnostic)]
-        diag.note(self.0);
-    }
-}
-
 #[derive(Subdiagnostic)]
 #[suggestion(
     ast_lowering_invalid_abi_suggestion,
-    code = "{suggestion}",
-    applicability = "maybe-incorrect"
+    code = "\"{suggestion}\"",
+    applicability = "maybe-incorrect",
+    style = "verbose"
 )]
 pub(crate) struct InvalidAbiSuggestion {
     #[primary_span]
@@ -212,12 +198,13 @@ pub(crate) struct InvalidRegister<'a> {
 }
 
 #[derive(Diagnostic)]
+#[note]
 #[diag(ast_lowering_invalid_register_class)]
-pub(crate) struct InvalidRegisterClass<'a> {
+pub(crate) struct InvalidRegisterClass {
     #[primary_span]
     pub op_span: Span,
     pub reg_class: Symbol,
-    pub error: &'a str,
+    pub supported_register_classes: String,
 }
 
 #[derive(Diagnostic)]
@@ -386,24 +373,39 @@ pub(crate) struct InclusiveRangeWithNoEnd {
     pub span: Span,
 }
 
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    ast_lowering_bad_return_type_notation_output_suggestion,
+    applicability = "machine-applicable",
+    style = "verbose"
+)]
+/// Given `T: Tr<m() -> Ret>` or `T: Tr<m(Ty) -> Ret>`, suggest `T: Tr<m(..)>`.
+pub(crate) struct RTNSuggestion {
+    #[suggestion_part(code = "")]
+    pub output: Span,
+    #[suggestion_part(code = "(..)")]
+    pub input: Span,
+}
+
 #[derive(Diagnostic)]
 pub(crate) enum BadReturnTypeNotation {
     #[diag(ast_lowering_bad_return_type_notation_inputs)]
     Inputs {
         #[primary_span]
-        #[suggestion(code = "()", applicability = "maybe-incorrect")]
+        #[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
         span: Span,
     },
     #[diag(ast_lowering_bad_return_type_notation_output)]
     Output {
         #[primary_span]
-        #[suggestion(code = "", applicability = "maybe-incorrect")]
         span: Span,
+        #[subdiagnostic]
+        suggestion: RTNSuggestion,
     },
     #[diag(ast_lowering_bad_return_type_notation_needs_dots)]
     NeedsDots {
         #[primary_span]
-        #[suggestion(code = "(..)", applicability = "maybe-incorrect")]
+        #[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
         span: Span,
     },
     #[diag(ast_lowering_bad_return_type_notation_position)]
@@ -442,14 +444,6 @@ pub(crate) struct NoPreciseCapturesOnApit {
     pub span: Span,
 }
 
-#[derive(Diagnostic)]
-#[diag(ast_lowering_no_precise_captures_on_rpitit)]
-#[note]
-pub(crate) struct NoPreciseCapturesOnRpitit {
-    #[primary_span]
-    pub span: Span,
-}
-
 #[derive(Diagnostic)]
 #[diag(ast_lowering_yield_in_closure)]
 pub(crate) struct YieldInClosure {
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index a76ca6772e52..8e1a3cd14354 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1,11 +1,11 @@
 use std::assert_matches::assert_matches;
 use std::ops::ControlFlow;
+use std::sync::Arc;
 
 use rustc_ast::ptr::P as AstP;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::expr_to_string;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_data_structures::sync::Lrc;
 use rustc_hir as hir;
 use rustc_hir::HirId;
 use rustc_hir::def::{DefKind, Res};
@@ -13,7 +13,7 @@ use rustc_middle::span_bug;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::errors::report_lit_error;
 use rustc_span::source_map::{Spanned, respan};
-use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
+use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 use visit::{Visitor, walk_expr};
 
@@ -74,15 +74,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     // Merge attributes into the inner expression.
                     if !e.attrs.is_empty() {
                         let old_attrs = self.attrs.get(&ex.hir_id.local_id).copied().unwrap_or(&[]);
-                        self.attrs.insert(
-                            ex.hir_id.local_id,
-                            &*self.arena.alloc_from_iter(
-                                e.attrs
-                                    .iter()
-                                    .map(|a| self.lower_attr(a))
-                                    .chain(old_attrs.iter().cloned()),
-                            ),
+                        let attrs = &*self.arena.alloc_from_iter(
+                            self.lower_attrs_vec(&e.attrs, e.span)
+                                .into_iter()
+                                .chain(old_attrs.iter().cloned()),
                         );
+                        if attrs.is_empty() {
+                            return ex;
+                        }
+
+                        self.attrs.insert(ex.hir_id.local_id, attrs);
                     }
                     return ex;
                 }
@@ -98,7 +99,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
 
             let expr_hir_id = self.lower_node_id(e.id);
-            self.lower_attrs(expr_hir_id, &e.attrs);
+            self.lower_attrs(expr_hir_id, &e.attrs, e.span);
 
             let kind = match &e.kind {
                 ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
@@ -147,7 +148,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ExprKind::IncludedBytes(bytes) => {
                     let lit = self.arena.alloc(respan(
                         self.lower_span(e.span),
-                        LitKind::ByteStr(Lrc::clone(bytes), StrStyle::Cooked),
+                        LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked),
                     ));
                     hir::ExprKind::Lit(lit)
                 }
@@ -208,6 +209,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     },
                 ),
                 ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
+                ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr),
                 ExprKind::Closure(box Closure {
                     binder,
                     capture_clause,
@@ -274,7 +276,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }
                 ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span),
                 ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp(
-                    self.lower_binop(*op),
+                    self.lower_assign_op(*op),
                     self.lower_expr(el),
                     self.lower_expr(er),
                 ),
@@ -284,9 +286,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ExprKind::Index(el, er, brackets_span) => {
                     hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span)
                 }
-                ExprKind::Range(Some(e1), Some(e2), RangeLimits::Closed) => {
-                    self.lower_expr_range_closed(e.span, e1, e2)
-                }
                 ExprKind::Range(e1, e2, lims) => {
                     self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims)
                 }
@@ -314,8 +313,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
                 }
                 ExprKind::Ret(e) => {
-                    let e = e.as_ref().map(|x| self.lower_expr(x));
-                    hir::ExprKind::Ret(e)
+                    let expr = e.as_ref().map(|x| self.lower_expr(x));
+                    self.checked_return(expr)
                 }
                 ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
                 ExprKind::Become(sub_expr) => {
@@ -354,7 +353,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         rest,
                     )
                 }
-                ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
+                ExprKind::Yield(kind) => self.lower_expr_yield(e.span, kind.expr().map(|x| &**x)),
                 ExprKind::Err(guar) => hir::ExprKind::Err(*guar),
 
                 ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast(
@@ -382,6 +381,36 @@ impl<'hir> LoweringContext<'_, 'hir> {
         })
     }
 
+    /// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
+    /// a contract ensures clause, if it exists.
+    fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
+        let checked_ret =
+            if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
+                let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
+                Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
+            } else {
+                opt_expr
+            };
+        hir::ExprKind::Ret(checked_ret)
+    }
+
+    /// Wraps an expression with a call to the ensures check before it gets returned.
+    pub(crate) fn inject_ensures_check(
+        &mut self,
+        expr: &'hir hir::Expr<'hir>,
+        span: Span,
+        cond_ident: Ident,
+        cond_hir_id: HirId,
+    ) -> &'hir hir::Expr<'hir> {
+        let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
+        let call_expr = self.expr_call_lang_item_fn_mut(
+            span,
+            hir::LangItem::ContractCheckEnsures,
+            arena_vec![self; *cond_fn, *expr],
+        );
+        self.arena.alloc(call_expr)
+    }
+
     pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
         self.with_new_scopes(c.value.span, |this| {
             let def_id = this.local_def_id(c.id);
@@ -420,6 +449,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         Spanned { node: b.node, span: self.lower_span(b.span) }
     }
 
+    fn lower_assign_op(&mut self, a: AssignOp) -> AssignOp {
+        Spanned { node: a.node, span: self.lower_span(a.span) }
+    }
+
     fn lower_legacy_const_generics(
         &mut self,
         mut f: Expr,
@@ -461,7 +494,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             if legacy_args_idx.contains(&idx) {
                 let parent_def_id = self.current_hir_id_owner.def_id;
                 let node_id = self.next_node_id();
-                self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, f.span);
+                self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, f.span);
                 let mut visitor = WillCreateDefIdsVisitor {};
                 let const_value = if let ControlFlow::Break(span) = visitor.visit_expr(&arg) {
                     AstP(Expr {
@@ -602,7 +635,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     this.mark_span_with_reason(
                         DesugaringKind::TryBlock,
                         expr.span,
-                        Some(Lrc::clone(&this.allow_try_trait)),
+                        Some(Arc::clone(&this.allow_try_trait)),
                     ),
                     expr,
                 )
@@ -610,7 +643,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let try_span = this.mark_span_with_reason(
                     DesugaringKind::TryBlock,
                     this.tcx.sess.source_map().end_point(body.span),
-                    Some(Lrc::clone(&this.allow_try_trait)),
+                    Some(Arc::clone(&this.allow_try_trait)),
                 );
 
                 (try_span, this.expr_unit(try_span))
@@ -647,12 +680,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
         let hir_id = self.next_id();
         let span = self.lower_span(arm.span);
-        self.lower_attrs(hir_id, &arm.attrs);
+        self.lower_attrs(hir_id, &arm.attrs, arm.span);
         let is_never_pattern = pat.is_never_pattern();
-        let body = if let Some(body) = &arm.body
+        // We need to lower the body even if it's unneeded for never pattern in match,
+        // ensure that we can get HirId for DefId if need (issue #137708).
+        let body = arm.body.as_ref().map(|x| self.lower_expr(x));
+        let body = if let Some(body) = body
             && !is_never_pattern
         {
-            self.lower_expr(body)
+            body
         } else {
             // Either `body.is_none()` or `is_never_pattern` here.
             if !is_never_pattern {
@@ -719,7 +755,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let unstable_span = self.mark_span_with_reason(
                     DesugaringKind::Async,
                     self.lower_span(span),
-                    Some(Lrc::clone(&self.allow_gen_future)),
+                    Some(Arc::clone(&self.allow_gen_future)),
                 );
                 let resume_ty =
                     self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span, None);
@@ -803,17 +839,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
             let unstable_span = self.mark_span_with_reason(
                 DesugaringKind::Async,
                 span,
-                Some(Lrc::clone(&self.allow_gen_future)),
+                Some(Arc::clone(&self.allow_gen_future)),
+            );
+            self.lower_attrs(
+                inner_hir_id,
+                &[Attribute {
+                    kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new(
+                        sym::track_caller,
+                        span,
+                    )))),
+                    id: self.tcx.sess.psess.attr_id_generator.mk_attr_id(),
+                    style: AttrStyle::Outer,
+                    span: unstable_span,
+                }],
+                span,
             );
-            self.lower_attrs(inner_hir_id, &[Attribute {
-                kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new(
-                    sym::track_caller,
-                    span,
-                )))),
-                id: self.tcx.sess.psess.attr_id_generator.mk_attr_id(),
-                style: AttrStyle::Outer,
-                span: unstable_span,
-            }]);
         }
     }
 
@@ -879,13 +919,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let features = match await_kind {
             FutureKind::Future => None,
-            FutureKind::AsyncIterator => Some(Lrc::clone(&self.allow_for_await)),
+            FutureKind::AsyncIterator => Some(Arc::clone(&self.allow_for_await)),
         };
         let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, features);
         let gen_future_span = self.mark_span_with_reason(
             DesugaringKind::Await,
             full_span,
-            Some(Lrc::clone(&self.allow_gen_future)),
+            Some(Arc::clone(&self.allow_gen_future)),
         );
         let expr_hir_id = expr.hir_id;
 
@@ -1038,6 +1078,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         )
     }
 
+    fn lower_expr_use(&mut self, use_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
+        hir::ExprKind::Use(self.lower_expr(expr), use_kw_span)
+    }
+
     fn lower_expr_closure(
         &mut self,
         binder: &ClosureBinder,
@@ -1064,7 +1108,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
             } else {
                 None
             };
-            let body_id = this.lower_fn_body(decl, |this| {
+            // FIXME(contracts): Support contracts on closures?
+            let body_id = this.lower_fn_body(decl, None, |this| {
                 this.coroutine_kind = coroutine_kind;
                 let e = this.lower_expr_mut(body);
                 coroutine_kind = this.coroutine_kind;
@@ -1391,7 +1436,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         None,
                     );
                     // Destructure like a unit struct.
-                    let unit_struct_pat = hir::PatKind::Path(qpath);
+                    let unit_struct_pat = hir::PatKind::Expr(self.arena.alloc(hir::PatExpr {
+                        kind: hir::PatExprKind::Path(qpath),
+                        hir_id: self.next_id(),
+                        span: self.lower_span(lhs.span),
+                    }));
                     return self.pat_without_dbm(lhs.span, unit_struct_pat);
                 }
             }
@@ -1508,15 +1557,39 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let lang_item = match (e1, e2, lims) {
             (None, None, HalfOpen) => hir::LangItem::RangeFull,
-            (Some(..), None, HalfOpen) => hir::LangItem::RangeFrom,
+            (Some(..), None, HalfOpen) => {
+                if self.tcx.features().new_range() {
+                    hir::LangItem::RangeFromCopy
+                } else {
+                    hir::LangItem::RangeFrom
+                }
+            }
             (None, Some(..), HalfOpen) => hir::LangItem::RangeTo,
-            (Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
+            (Some(..), Some(..), HalfOpen) => {
+                if self.tcx.features().new_range() {
+                    hir::LangItem::RangeCopy
+                } else {
+                    hir::LangItem::Range
+                }
+            }
             (None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
-            (Some(..), Some(..), Closed) => unreachable!(),
+            (Some(e1), Some(e2), Closed) => {
+                if self.tcx.features().new_range() {
+                    hir::LangItem::RangeInclusiveCopy
+                } else {
+                    return self.lower_expr_range_closed(span, e1, e2);
+                }
+            }
             (start, None, Closed) => {
                 self.dcx().emit_err(InclusiveRangeWithNoEnd { span });
                 match start {
-                    Some(..) => hir::LangItem::RangeFrom,
+                    Some(..) => {
+                        if self.tcx.features().new_range() {
+                            hir::LangItem::RangeFromCopy
+                        } else {
+                            hir::LangItem::RangeFrom
+                        }
+                    }
                     None => hir::LangItem::RangeFull,
                 }
             }
@@ -1618,7 +1691,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> {
         let hir_id = self.lower_node_id(f.id);
-        self.lower_attrs(hir_id, &f.attrs);
+        self.lower_attrs(hir_id, &f.attrs, f.span);
         hir::ExprField {
             hir_id,
             ident: self.lower_ident(f.ident),
@@ -1632,6 +1705,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let yielded =
             opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
 
+        if !self.tcx.features().yield_expr()
+            && !self.tcx.features().coroutines()
+            && !self.tcx.features().gen_blocks()
+        {
+            rustc_session::parse::feature_err(
+                &self.tcx.sess,
+                sym::yield_expr,
+                span,
+                fluent_generated::ast_lowering_yield,
+            )
+            .emit();
+        }
+
         let is_async_gen = match self.coroutine_kind {
             Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => false,
             Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => true,
@@ -1656,28 +1742,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     None,
                 );
             }
-            Some(hir::CoroutineKind::Coroutine(_)) => {
-                if !self.tcx.features().coroutines() {
-                    rustc_session::parse::feature_err(
-                        &self.tcx.sess,
-                        sym::coroutines,
-                        span,
-                        fluent_generated::ast_lowering_yield,
-                    )
-                    .emit();
-                }
-                false
-            }
+            Some(hir::CoroutineKind::Coroutine(_)) => false,
             None => {
-                if !self.tcx.features().coroutines() {
-                    rustc_session::parse::feature_err(
-                        &self.tcx.sess,
-                        sym::coroutines,
-                        span,
-                        fluent_generated::ast_lowering_yield,
-                    )
-                    .emit();
-                }
                 let suggestion = self.current_item.map(|s| s.shrink_to_lo());
                 self.dcx().emit_err(YieldInClosure { span, suggestion });
                 self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable));
@@ -1881,7 +1947,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         //
         // Also, add the attributes to the outer returned expr node.
         let expr = self.expr_drop_temps_mut(for_span, match_expr);
-        self.lower_attrs(expr.hir_id, &e.attrs);
+        self.lower_attrs(expr.hir_id, &e.attrs, e.span);
         expr
     }
 
@@ -1901,13 +1967,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let unstable_span = self.mark_span_with_reason(
             DesugaringKind::QuestionMark,
             span,
-            Some(Lrc::clone(&self.allow_try_trait)),
+            Some(Arc::clone(&self.allow_try_trait)),
         );
         let try_span = self.tcx.sess.source_map().end_point(span);
         let try_span = self.mark_span_with_reason(
             DesugaringKind::QuestionMark,
             try_span,
-            Some(Lrc::clone(&self.allow_try_trait)),
+            Some(Arc::clone(&self.allow_try_trait)),
         );
 
         // `Try::branch(<expr>)`
@@ -1938,7 +2004,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             let val_ident = Ident::with_dummy_span(sym::val);
             let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
             let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
-            self.lower_attrs(val_expr.hir_id, &attrs);
+            self.lower_attrs(val_expr.hir_id, &attrs, span);
             let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
             self.arm(continue_pat, val_expr)
         };
@@ -1966,9 +2032,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     ),
                 ))
             } else {
-                self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr))))
+                let ret_expr = self.checked_return(Some(from_residual_expr));
+                self.arena.alloc(self.expr(try_span, ret_expr))
             };
-            self.lower_attrs(ret_expr.hir_id, &attrs);
+            self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span);
 
             let break_pat = self.pat_cf_break(try_span, residual_local);
             self.arm(break_pat, ret_expr)
@@ -2001,7 +2068,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let unstable_span = self.mark_span_with_reason(
             DesugaringKind::YeetExpr,
             span,
-            Some(Lrc::clone(&self.allow_try_trait)),
+            Some(Arc::clone(&self.allow_try_trait)),
         );
 
         let from_yeet_expr = self.wrap_in_try_constructor(
@@ -2015,7 +2082,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             let target_id = Ok(catch_id);
             hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
         } else {
-            hir::ExprKind::Ret(Some(from_yeet_expr))
+            self.checked_return(Some(from_yeet_expr))
         }
     }
 
@@ -2073,31 +2140,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
     }
 
-    pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> {
+    fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> {
         let lit = self.arena.alloc(hir::Lit {
             span: sp,
-            node: ast::LitKind::Int(
-                (value as u128).into(),
-                ast::LitIntType::Unsigned(ast::UintTy::Usize),
-            ),
+            node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)),
         });
         self.expr(sp, hir::ExprKind::Lit(lit))
     }
 
+    pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> {
+        self.expr_uint(sp, ast::UintTy::Usize, value as u128)
+    }
+
     pub(super) fn expr_u32(&mut self, sp: Span, value: u32) -> hir::Expr<'hir> {
-        let lit = self.arena.alloc(hir::Lit {
-            span: sp,
-            node: ast::LitKind::Int(
-                u128::from(value).into(),
-                ast::LitIntType::Unsigned(ast::UintTy::U32),
-            ),
-        });
-        self.expr(sp, hir::ExprKind::Lit(lit))
+        self.expr_uint(sp, ast::UintTy::U32, value as u128)
     }
 
-    pub(super) fn expr_char(&mut self, sp: Span, value: char) -> hir::Expr<'hir> {
-        let lit = self.arena.alloc(hir::Lit { span: sp, node: ast::LitKind::Char(value) });
-        self.expr(sp, hir::ExprKind::Lit(lit))
+    pub(super) fn expr_u16(&mut self, sp: Span, value: u16) -> hir::Expr<'hir> {
+        self.expr_uint(sp, ast::UintTy::U16, value as u128)
     }
 
     pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> {
@@ -2125,7 +2185,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.arena.alloc(self.expr_call_mut(span, e, args))
     }
 
-    fn expr_call_lang_item_fn_mut(
+    pub(super) fn expr_call_lang_item_fn_mut(
         &mut self,
         span: Span,
         lang_item: hir::LangItem,
@@ -2135,7 +2195,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.expr_call_mut(span, path, args)
     }
 
-    fn expr_call_lang_item_fn(
+    pub(super) fn expr_call_lang_item_fn(
         &mut self,
         span: Span,
         lang_item: hir::LangItem,
@@ -2159,7 +2219,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let path = hir::ExprKind::Path(hir::QPath::TypeRelative(
             self.arena.alloc(self.ty(span, hir::TyKind::Path(qpath))),
             self.arena.alloc(hir::PathSegment::new(
-                Ident::new(name, span),
+                Ident::new(name, self.lower_span(span)),
                 self.next_id(),
                 Res::Err,
             )),
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 22aa1e6fc201..0de0319c6676 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -292,7 +292,7 @@ fn make_count<'hir>(
                 hir::LangItem::FormatCount,
                 sym::Is,
             ));
-            let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
+            let value = ctx.arena.alloc_from_iter([ctx.expr_u16(sp, *n)]);
             ctx.expr_call_mut(sp, count_is, value)
         }
         Some(FormatCount::Argument(arg)) => {
@@ -323,14 +323,12 @@ fn make_count<'hir>(
 /// Generates
 ///
 /// ```text
-///     <core::fmt::rt::Placeholder::new(
-///         …usize, // position
-///         '…', // fill
-///         <core::fmt::rt::Alignment>::…, // alignment
-///         …u32, // flags
-///         <core::fmt::rt::Count::…>, // width
-///         <core::fmt::rt::Count::…>, // precision
-///     )
+///     <core::fmt::rt::Placeholder {
+///         position: …usize,
+///         flags: …u32,
+///         precision: <core::fmt::rt::Count::…>,
+///         width: <core::fmt::rt::Count::…>,
+///     }
 /// ```
 fn make_format_spec<'hir>(
     ctx: &mut LoweringContext<'_, 'hir>,
@@ -361,31 +359,36 @@ fn make_format_spec<'hir>(
         zero_pad,
         debug_hex,
     } = &placeholder.format_options;
-    let fill = ctx.expr_char(sp, fill.unwrap_or(' '));
-    let align =
-        ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatAlignment, match alignment {
-            Some(FormatAlignment::Left) => sym::Left,
-            Some(FormatAlignment::Right) => sym::Right,
-            Some(FormatAlignment::Center) => sym::Center,
-            None => sym::Unknown,
-        });
-    // This needs to match `Flag` in library/core/src/fmt/rt.rs.
-    let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
-        | ((sign == Some(FormatSign::Minus)) as u32) << 1
-        | (alternate as u32) << 2
-        | (zero_pad as u32) << 3
-        | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
-        | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
+    let fill = fill.unwrap_or(' ');
+    // These need to match the constants in library/core/src/fmt/rt.rs.
+    let align = match alignment {
+        Some(FormatAlignment::Left) => 0,
+        Some(FormatAlignment::Right) => 1,
+        Some(FormatAlignment::Center) => 2,
+        None => 3,
+    };
+    // This needs to match the constants in library/core/src/fmt/rt.rs.
+    let flags: u32 = fill as u32
+        | ((sign == Some(FormatSign::Plus)) as u32) << 21
+        | ((sign == Some(FormatSign::Minus)) as u32) << 22
+        | (alternate as u32) << 23
+        | (zero_pad as u32) << 24
+        | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
+        | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
+        | (width.is_some() as u32) << 27
+        | (precision.is_some() as u32) << 28
+        | align << 29
+        | 1 << 31; // Highest bit always set.
     let flags = ctx.expr_u32(sp, flags);
     let precision = make_count(ctx, sp, precision, argmap);
     let width = make_count(ctx, sp, width, argmap);
-    let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
-        sp,
-        hir::LangItem::FormatPlaceholder,
-        sym::new,
-    ));
-    let args = ctx.arena.alloc_from_iter([position, fill, align, flags, precision, width]);
-    ctx.expr_call_mut(sp, format_placeholder_new, args)
+    let position = ctx.expr_field(Ident::new(sym::position, sp), ctx.arena.alloc(position), sp);
+    let flags = ctx.expr_field(Ident::new(sym::flags, sp), ctx.arena.alloc(flags), sp);
+    let precision = ctx.expr_field(Ident::new(sym::precision, sp), ctx.arena.alloc(precision), sp);
+    let width = ctx.expr_field(Ident::new(sym::width, sp), ctx.arena.alloc(width), sp);
+    let placeholder = ctx.arena.alloc(hir::QPath::LangItem(hir::LangItem::FormatPlaceholder, sp));
+    let fields = ctx.arena.alloc_from_iter([position, flags, precision, width]);
+    ctx.expr(sp, hir::ExprKind::Struct(placeholder, fields, hir::StructTailExpr::None))
 }
 
 fn expand_format_args<'hir>(
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 29d4fb9ef259..26c0e7e5f82a 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -1,3 +1,4 @@
+use intravisit::InferKind;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_hir as hir;
 use rustc_hir::def_id::{LocalDefId, LocalDefIdMap};
@@ -78,24 +79,31 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
 
         // Make sure that the DepNode of some node coincides with the HirId
         // owner of that node.
-        if cfg!(debug_assertions) && hir_id.owner != self.owner {
-            span_bug!(
-                span,
-                "inconsistent HirId at `{:?}` for `{:?}`: \
+        if cfg!(debug_assertions) {
+            if hir_id.owner != self.owner {
+                span_bug!(
+                    span,
+                    "inconsistent HirId at `{:?}` for `{node:?}`: \
                      current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})",
-                self.tcx.sess.source_map().span_to_diagnostic_string(span),
-                node,
-                self.tcx
-                    .definitions_untracked()
-                    .def_path(self.owner.def_id)
-                    .to_string_no_crate_verbose(),
-                self.owner,
-                self.tcx
-                    .definitions_untracked()
-                    .def_path(hir_id.owner.def_id)
-                    .to_string_no_crate_verbose(),
-                hir_id.owner,
-            )
+                    self.tcx.sess.source_map().span_to_diagnostic_string(span),
+                    self.tcx
+                        .definitions_untracked()
+                        .def_path(self.owner.def_id)
+                        .to_string_no_crate_verbose(),
+                    self.owner,
+                    self.tcx
+                        .definitions_untracked()
+                        .def_path(hir_id.owner.def_id)
+                        .to_string_no_crate_verbose(),
+                    hir_id.owner,
+                )
+            }
+            if self.tcx.sess.opts.incremental.is_some()
+                && span.parent().is_none()
+                && !span.is_dummy()
+            {
+                span_bug!(span, "span without a parent: {:#?}, {node:?}", span.data())
+            }
         }
 
         self.nodes[hir_id.local_id] = ParentedNode { parent: self.parent_node, node };
@@ -156,7 +164,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
     fn visit_item(&mut self, i: &'hir Item<'hir>) {
         debug_assert_eq!(i.owner_id, self.owner);
         self.with_parent(i.hir_id(), |this| {
-            if let ItemKind::Struct(struct_def, _) = &i.kind {
+            if let ItemKind::Struct(_, struct_def, _) = &i.kind {
                 // If this is a tuple or unit-like struct, register the constructor.
                 if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
                     this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def));
@@ -258,14 +266,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
-    fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) {
-        self.insert(const_arg.span(), const_arg.hir_id, Node::ConstArg(const_arg));
-
-        self.with_parent(const_arg.hir_id, |this| {
-            intravisit::walk_const_arg(this, const_arg);
-        });
-    }
-
     fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
         self.insert(expr.span, expr.hir_id, Node::Expr(expr));
 
@@ -295,22 +295,41 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         intravisit::walk_path_segment(self, path_segment);
     }
 
-    fn visit_ty(&mut self, ty: &'hir Ty<'hir>) {
-        self.insert(ty.span, ty.hir_id, Node::Ty(ty));
+    fn visit_ty(&mut self, ty: &'hir Ty<'hir, AmbigArg>) {
+        self.insert(ty.span, ty.hir_id, Node::Ty(ty.as_unambig_ty()));
 
         self.with_parent(ty.hir_id, |this| {
             intravisit::walk_ty(this, ty);
         });
     }
 
-    fn visit_infer(&mut self, inf: &'hir InferArg) {
-        self.insert(inf.span, inf.hir_id, Node::Infer(inf));
+    fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir, AmbigArg>) {
+        self.insert(
+            const_arg.as_unambig_ct().span(),
+            const_arg.hir_id,
+            Node::ConstArg(const_arg.as_unambig_ct()),
+        );
 
-        self.with_parent(inf.hir_id, |this| {
-            intravisit::walk_inf(this, inf);
+        self.with_parent(const_arg.hir_id, |this| {
+            intravisit::walk_ambig_const_arg(this, const_arg);
         });
     }
 
+    fn visit_infer(
+        &mut self,
+        inf_id: HirId,
+        inf_span: Span,
+        kind: InferKind<'hir>,
+    ) -> Self::Result {
+        match kind {
+            InferKind::Ty(ty) => self.insert(inf_span, inf_id, Node::Ty(ty)),
+            InferKind::Const(ct) => self.insert(inf_span, inf_id, Node::ConstArg(ct)),
+            InferKind::Ambig(inf) => self.insert(inf_span, inf_id, Node::Infer(inf)),
+        }
+
+        self.visit_id(inf_id);
+    }
+
     fn visit_trait_ref(&mut self, tr: &'hir TraitRef<'hir>) {
         self.insert(tr.path.span, tr.hir_ref_id, Node::TraitRef(tr));
 
@@ -393,8 +412,12 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
-    fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) {
-        self.visit_pat(p)
+    fn visit_pattern_type_pattern(&mut self, pat: &'hir hir::TyPat<'hir>) {
+        self.insert(pat.span, pat.hir_id, Node::TyPat(pat));
+
+        self.with_parent(pat.hir_id, |this| {
+            intravisit::walk_ty_pat(this, pat);
+        });
     }
 
     fn visit_precise_capturing_arg(
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 61d7da429f8e..d8b55bea3d76 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1,25 +1,23 @@
+use rustc_abi::ExternAbi;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::AssocCtxt;
 use rustc_ast::*;
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
-use rustc_hir::PredicateOrigin;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
+use rustc_hir::{self as hir, HirId, IsAnonInPath, PredicateOrigin};
 use rustc_index::{IndexSlice, IndexVec};
-use rustc_middle::span_bug;
 use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
 use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym};
-use rustc_target::spec::abi;
+use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 use tracing::instrument;
 
 use super::errors::{
-    InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound,
-    TupleStructWithDefault,
+    InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound, TupleStructWithDefault,
 };
+use super::stability::{enabled_names, gate_unstable_abi};
 use super::{
     AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
     ResolverAstLoweringExt,
@@ -93,7 +91,8 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
         debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
         self.with_lctx(CRATE_NODE_ID, |lctx| {
             let module = lctx.lower_mod(&c.items, &c.spans);
-            lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
+            // FIXME(jdonszelman): is dummy span ever a problem here?
+            lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP);
             hir::OwnerNode::Crate(module)
         })
     }
@@ -104,10 +103,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
     }
 
     fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) {
-        let def_id = self.resolver.node_id_to_def_id[&item.id];
-        let parent_id = self.tcx.local_parent(def_id);
-        let parent_hir = self.lower_node(parent_id).unwrap();
-        self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt, parent_hir))
+        self.with_lctx(item.id, |lctx| lctx.lower_assoc_item(item, ctxt))
     }
 
     fn lower_foreign_item(&mut self, item: &ForeignItem) {
@@ -131,8 +127,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     pub(super) fn lower_item_ref(&mut self, i: &Item) -> SmallVec<[hir::ItemId; 1]> {
-        let mut node_ids =
-            smallvec![hir::ItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } }];
+        let mut node_ids = smallvec![hir::ItemId { owner_id: self.owner_id(i.id) }];
         if let ItemKind::Use(use_tree) = &i.kind {
             self.lower_item_id_use_tree(use_tree, &mut node_ids);
         }
@@ -143,9 +138,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         match &tree.kind {
             UseTreeKind::Nested { items, .. } => {
                 for &(ref nested, id) in items {
-                    vec.push(hir::ItemId {
-                        owner_id: hir::OwnerId { def_id: self.local_def_id(id) },
-                    });
+                    vec.push(hir::ItemId { owner_id: self.owner_id(id) });
                     self.lower_item_id_use_tree(nested, vec);
                 }
             }
@@ -154,14 +147,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
-        let mut ident = i.ident;
         let vis_span = self.lower_span(i.vis.span);
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
-        let attrs = self.lower_attrs(hir_id, &i.attrs);
-        let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
+        let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
+        let kind = self.lower_item_kind(i.span, i.id, hir_id, attrs, vis_span, &i.kind);
         let item = hir::Item {
             owner_id: hir_id.expect_owner(),
-            ident: self.lower_ident(ident),
             kind,
             vis_span,
             span: self.lower_span(i.span),
@@ -174,25 +165,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
         span: Span,
         id: NodeId,
         hir_id: hir::HirId,
-        ident: &mut Ident,
         attrs: &'hir [hir::Attribute],
         vis_span: Span,
         i: &ItemKind,
     ) -> hir::ItemKind<'hir> {
         match i {
-            ItemKind::ExternCrate(orig_name) => hir::ItemKind::ExternCrate(*orig_name),
+            ItemKind::ExternCrate(orig_name, ident) => {
+                let ident = self.lower_ident(*ident);
+                hir::ItemKind::ExternCrate(*orig_name, ident)
+            }
             ItemKind::Use(use_tree) => {
                 // Start with an empty prefix.
                 let prefix = Path { segments: ThinVec::new(), span: use_tree.span, tokens: None };
 
-                self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs)
+                self.lower_use_tree(use_tree, &prefix, id, vis_span, attrs)
             }
-            ItemKind::Static(box ast::StaticItem { ty: t, safety: _, mutability: m, expr: e }) => {
+            ItemKind::Static(box ast::StaticItem {
+                ident,
+                ty: t,
+                safety: _,
+                mutability: m,
+                expr: e,
+                define_opaque,
+            }) => {
+                let ident = self.lower_ident(*ident);
                 let (ty, body_id) =
                     self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy);
-                hir::ItemKind::Static(ty, *m, body_id)
+                self.lower_define_opaque(hir_id, define_opaque);
+                hir::ItemKind::Static(ident, ty, *m, body_id)
             }
-            ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
+            ItemKind::Const(box ast::ConstItem {
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+                ..
+            }) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, (ty, body_id)) = self.lower_generics(
                     generics,
                     id,
@@ -201,12 +211,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         this.lower_const_item(ty, span, expr.as_deref(), ImplTraitPosition::ConstTy)
                     },
                 );
-                hir::ItemKind::Const(ty, generics, body_id)
+                self.lower_define_opaque(hir_id, &define_opaque);
+                hir::ItemKind::Const(ident, ty, generics, body_id)
             }
             ItemKind::Fn(box Fn {
                 sig: FnSig { decl, header, span: fn_sig_span },
+                ident,
                 generics,
                 body,
+                contract,
+                define_opaque,
                 ..
             }) => {
                 self.with_new_scopes(*fn_sig_span, |this| {
@@ -223,6 +237,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         coroutine_kind,
                         body.as_deref(),
                         attrs,
+                        contract.as_deref(),
                     );
 
                     let itctx = ImplTraitContext::Universal;
@@ -234,23 +249,39 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         header: this.lower_fn_header(*header, hir::Safety::Safe, attrs),
                         span: this.lower_span(*fn_sig_span),
                     };
-                    hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() }
+                    this.lower_define_opaque(hir_id, define_opaque);
+                    let ident = this.lower_ident(*ident);
+                    hir::ItemKind::Fn {
+                        ident,
+                        sig,
+                        generics,
+                        body: body_id,
+                        has_body: body.is_some(),
+                    }
                 })
             }
-            ItemKind::Mod(_, mod_kind) => match mod_kind {
-                ModKind::Loaded(items, _, spans, _) => {
-                    hir::ItemKind::Mod(self.lower_mod(items, spans))
+            ItemKind::Mod(_, ident, mod_kind) => {
+                let ident = self.lower_ident(*ident);
+                match mod_kind {
+                    ModKind::Loaded(items, _, spans, _) => {
+                        hir::ItemKind::Mod(ident, self.lower_mod(items, spans))
+                    }
+                    ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
                 }
-                ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
-            },
+            }
             ItemKind::ForeignMod(fm) => hir::ItemKind::ForeignMod {
-                abi: fm.abi.map_or(abi::Abi::FALLBACK, |abi| self.lower_abi(abi)),
+                abi: fm.abi.map_or(ExternAbi::FALLBACK, |abi| self.lower_abi(abi)),
                 items: self
                     .arena
                     .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
             },
-            ItemKind::GlobalAsm(asm) => hir::ItemKind::GlobalAsm(self.lower_inline_asm(span, asm)),
-            ItemKind::TyAlias(box TyAlias { generics, where_clauses, ty, .. }) => {
+            ItemKind::GlobalAsm(asm) => {
+                let asm = self.lower_inline_asm(span, asm);
+                let fake_body =
+                    self.lower_body(|this| (&[], this.expr(span, hir::ExprKind::InlineAsm(asm))));
+                hir::ItemKind::GlobalAsm { asm, fake_body }
+            }
+            ItemKind::TyAlias(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
                 // We lower
                 //
                 // type Foo = impl Trait
@@ -259,6 +290,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 //
                 // type Foo = Foo1
                 // opaque type Foo1: Trait
+                let ident = self.lower_ident(*ident);
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, *where_clauses, true);
                 let (generics, ty) = self.lower_generics(
@@ -273,17 +305,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             );
                             this.arena.alloc(this.ty(span, hir::TyKind::Err(guar)))
                         }
-                        Some(ty) => this.lower_ty(ty, ImplTraitContext::OpaqueTy {
-                            origin: hir::OpaqueTyOrigin::TyAlias {
-                                parent: this.local_def_id(id),
-                                in_assoc_ty: false,
+                        Some(ty) => this.lower_ty(
+                            ty,
+                            ImplTraitContext::OpaqueTy {
+                                origin: hir::OpaqueTyOrigin::TyAlias {
+                                    parent: this.local_def_id(id),
+                                    in_assoc_ty: false,
+                                },
                             },
-                        }),
+                        ),
                     },
                 );
-                hir::ItemKind::TyAlias(ty, generics)
+                hir::ItemKind::TyAlias(ident, ty, generics)
             }
-            ItemKind::Enum(enum_definition, generics) => {
+            ItemKind::Enum(ident, enum_definition, generics) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, variants) = self.lower_generics(
                     generics,
                     id,
@@ -294,25 +330,27 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         )
                     },
                 );
-                hir::ItemKind::Enum(hir::EnumDef { variants }, generics)
+                hir::ItemKind::Enum(ident, hir::EnumDef { variants }, generics)
             }
-            ItemKind::Struct(struct_def, generics) => {
+            ItemKind::Struct(ident, struct_def, generics) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, struct_def) = self.lower_generics(
                     generics,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, struct_def),
                 );
-                hir::ItemKind::Struct(struct_def, generics)
+                hir::ItemKind::Struct(ident, struct_def, generics)
             }
-            ItemKind::Union(vdata, generics) => {
+            ItemKind::Union(ident, vdata, generics) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, vdata) = self.lower_generics(
                     generics,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, vdata),
                 );
-                hir::ItemKind::Union(vdata, generics)
+                hir::ItemKind::Union(ident, vdata, generics)
             }
             ItemKind::Impl(box Impl {
                 safety,
@@ -363,10 +401,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         (trait_ref, lowered_ty)
                     });
 
-                self.is_in_trait_impl = trait_ref.is_some();
-                let new_impl_items = self
-                    .arena
-                    .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
+                let new_impl_items = self.arena.alloc_from_iter(
+                    impl_items
+                        .iter()
+                        .map(|item| self.lower_impl_item_ref(item, trait_ref.is_some())),
+                );
 
                 // `defaultness.has_value()` is never called for an `impl`, always `true` in order
                 // to not cause an assertion failure inside the `lower_defaultness` function.
@@ -388,7 +427,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     items: new_impl_items,
                 }))
             }
-            ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => {
+            ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, (safety, items, bounds)) = self.lower_generics(
                     generics,
                     id,
@@ -405,9 +445,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         (safety, items, bounds)
                     },
                 );
-                hir::ItemKind::Trait(*is_auto, safety, generics, bounds, items)
+                hir::ItemKind::Trait(*is_auto, safety, ident, generics, bounds, items)
             }
-            ItemKind::TraitAlias(generics, bounds) => {
+            ItemKind::TraitAlias(ident, generics, bounds) => {
+                let ident = self.lower_ident(*ident);
                 let (generics, bounds) = self.lower_generics(
                     generics,
                     id,
@@ -419,9 +460,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         )
                     },
                 );
-                hir::ItemKind::TraitAlias(generics, bounds)
+                hir::ItemKind::TraitAlias(ident, generics, bounds)
             }
-            ItemKind::MacroDef(MacroDef { body, macro_rules }) => {
+            ItemKind::MacroDef(ident, MacroDef { body, macro_rules }) => {
+                let ident = self.lower_ident(*ident);
                 let body = P(self.lower_delim_args(body));
                 let def_id = self.local_def_id(id);
                 let def_kind = self.tcx.def_kind(def_id);
@@ -432,11 +474,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     );
                 };
                 let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
-                hir::ItemKind::Macro(macro_def, macro_kind)
+                hir::ItemKind::Macro(ident, macro_def, macro_kind)
             }
             ItemKind::Delegation(box delegation) => {
-                let delegation_results = self.lower_delegation(delegation, id);
+                let delegation_results = self.lower_delegation(delegation, id, false);
                 hir::ItemKind::Fn {
+                    ident: delegation_results.ident,
                     sig: delegation_results.sig,
                     generics: delegation_results.generics,
                     body: delegation_results.body_id,
@@ -467,7 +510,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
         prefix: &Path,
         id: NodeId,
         vis_span: Span,
-        ident: &mut Ident,
         attrs: &'hir [hir::Attribute],
     ) -> hir::ItemKind<'hir> {
         let path = &tree.prefix;
@@ -475,7 +517,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         match tree.kind {
             UseTreeKind::Simple(rename) => {
-                *ident = tree.ident();
+                let mut ident = tree.ident();
 
                 // First, apply the prefix to the path.
                 let mut path = Path { segments, span: path.span, tokens: None };
@@ -486,13 +528,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 {
                     let _ = path.segments.pop();
                     if rename.is_none() {
-                        *ident = path.segments.last().unwrap().ident;
+                        ident = path.segments.last().unwrap().ident;
                     }
                 }
 
                 let res = self.lower_import_res(id, path.span);
                 let path = self.lower_use_path(res, &path, ParamMode::Explicit);
-                hir::ItemKind::Use(path, hir::UseKind::Single)
+                let ident = self.lower_ident(ident);
+                hir::ItemKind::Use(path, hir::UseKind::Single(ident))
             }
             UseTreeKind::Glob => {
                 let res = self.expect_full_res(id);
@@ -531,7 +574,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
                 // Add all the nested `PathListItem`s to the HIR.
                 for &(ref use_tree, id) in trees {
-                    let new_hir_id = self.local_def_id(id);
+                    let owner_id = self.owner_id(id);
 
                     // Each `use` import is an item and thus are owners of the
                     // names in the path. Up to this point the nested import is
@@ -539,20 +582,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     // own its own names, we have to adjust the owner before
                     // lowering the rest of the import.
                     self.with_hir_id_owner(id, |this| {
-                        let mut ident = *ident;
-
                         // `prefix` is lowered multiple times, but in different HIR owners.
                         // So each segment gets renewed `HirId` with the same
                         // `ItemLocalId` and the new owner. (See `lower_node_id`)
-                        let kind =
-                            this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
+                        let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, attrs);
                         if !attrs.is_empty() {
                             this.attrs.insert(hir::ItemLocalId::ZERO, attrs);
                         }
 
                         let item = hir::Item {
-                            owner_id: hir::OwnerId { def_id: new_hir_id },
-                            ident: this.lower_ident(ident),
+                            owner_id,
                             kind,
                             vis_span,
                             span: this.lower_span(use_tree.span),
@@ -581,77 +620,77 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    fn lower_assoc_item(
-        &mut self,
-        item: &AssocItem,
-        ctxt: AssocCtxt,
-        parent_hir: &'hir hir::OwnerInfo<'hir>,
-    ) -> hir::OwnerNode<'hir> {
-        let parent_item = parent_hir.node().expect_item();
-        match parent_item.kind {
-            hir::ItemKind::Impl(impl_) => {
-                self.is_in_trait_impl = impl_.of_trait.is_some();
-            }
-            hir::ItemKind::Trait(_, _, _, _, _) => {}
-            kind => {
-                span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr())
-            }
-        }
-
+    fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) -> hir::OwnerNode<'hir> {
         // Evaluate with the lifetimes in `params` in-scope.
         // This is used to track which lifetimes have already been defined,
         // and which need to be replicated when lowering an async fn.
         match ctxt {
             AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)),
-            AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)),
+            AssocCtxt::Impl { of_trait } => {
+                hir::OwnerNode::ImplItem(self.lower_impl_item(item, of_trait))
+            }
         }
     }
 
     fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
         let owner_id = hir_id.expect_owner();
-        let attrs = self.lower_attrs(hir_id, &i.attrs);
-        let item = hir::ForeignItem {
-            owner_id,
-            ident: self.lower_ident(i.ident),
-            kind: match &i.kind {
-                ForeignItemKind::Fn(box Fn { sig, generics, .. }) => {
-                    let fdec = &sig.decl;
-                    let itctx = ImplTraitContext::Universal;
-                    let (generics, (decl, fn_args)) =
-                        self.lower_generics(generics, i.id, itctx, |this| {
-                            (
-                                // Disallow `impl Trait` in foreign items.
-                                this.lower_fn_decl(
-                                    fdec,
-                                    i.id,
-                                    sig.span,
-                                    FnDeclKind::ExternFn,
-                                    None,
-                                ),
-                                this.lower_fn_params_to_names(fdec),
-                            )
-                        });
+        let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
+        let (ident, kind) = match &i.kind {
+            ForeignItemKind::Fn(box Fn { sig, ident, generics, define_opaque, .. }) => {
+                let fdec = &sig.decl;
+                let itctx = ImplTraitContext::Universal;
+                let (generics, (decl, fn_args)) =
+                    self.lower_generics(generics, i.id, itctx, |this| {
+                        (
+                            // Disallow `impl Trait` in foreign items.
+                            this.lower_fn_decl(fdec, i.id, sig.span, FnDeclKind::ExternFn, None),
+                            this.lower_fn_params_to_idents(fdec),
+                        )
+                    });
 
-                    // Unmarked safety in unsafe block defaults to unsafe.
-                    let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);
+                // Unmarked safety in unsafe block defaults to unsafe.
+                let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs);
 
+                if define_opaque.is_some() {
+                    self.dcx().span_err(i.span, "foreign functions cannot define opaque types");
+                }
+
+                (
+                    ident,
                     hir::ForeignItemKind::Fn(
                         hir::FnSig { header, decl, span: self.lower_span(sig.span) },
                         fn_args,
                         generics,
-                    )
+                    ),
+                )
+            }
+            ForeignItemKind::Static(box StaticItem {
+                ident,
+                ty,
+                mutability,
+                expr: _,
+                safety,
+                define_opaque,
+            }) => {
+                let ty =
+                    self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
+                let safety = self.lower_safety(*safety, hir::Safety::Unsafe);
+                if define_opaque.is_some() {
+                    self.dcx().span_err(i.span, "foreign statics cannot define opaque types");
                 }
-                ForeignItemKind::Static(box StaticItem { ty, mutability, expr: _, safety }) => {
-                    let ty = self
-                        .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy));
-                    let safety = self.lower_safety(*safety, hir::Safety::Unsafe);
+                (ident, hir::ForeignItemKind::Static(ty, *mutability, safety))
+            }
+            ForeignItemKind::TyAlias(box TyAlias { ident, .. }) => {
+                (ident, hir::ForeignItemKind::Type)
+            }
+            ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
+        };
 
-                    hir::ForeignItemKind::Static(ty, *mutability, safety)
-                }
-                ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type,
-                ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
-            },
+        let item = hir::ForeignItem {
+            owner_id,
+            ident: self.lower_ident(*ident),
+            kind,
             vis_span: self.lower_span(i.vis.span),
             span: self.lower_span(i.span),
         };
@@ -660,15 +699,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_foreign_item_ref(&mut self, i: &ForeignItem) -> hir::ForeignItemRef {
         hir::ForeignItemRef {
-            id: hir::ForeignItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } },
-            ident: self.lower_ident(i.ident),
+            id: hir::ForeignItemId { owner_id: self.owner_id(i.id) },
+            // `unwrap` is safe because `ForeignItemKind::MacCall` is the only foreign item kind
+            // without an identifier and it cannot reach here.
+            ident: self.lower_ident(i.kind.ident().unwrap()),
             span: self.lower_span(i.span),
         }
     }
 
     fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> {
         let hir_id = self.lower_node_id(v.id);
-        self.lower_attrs(hir_id, &v.attrs);
+        self.lower_attrs(hir_id, &v.attrs, v.span);
         hir::Variant {
             hir_id,
             def_id: self.local_def_id(v.id),
@@ -730,7 +771,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> hir::FieldDef<'hir> {
         let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy));
         let hir_id = self.lower_node_id(f.id);
-        self.lower_attrs(hir_id, &f.attrs);
+        self.lower_attrs(hir_id, &f.attrs, f.span);
         hir::FieldDef {
             span: self.lower_span(f.span),
             hir_id,
@@ -749,11 +790,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
-        let attrs = self.lower_attrs(hir_id, &i.attrs);
+        let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
         let trait_item_def_id = hir_id.expect_owner();
 
-        let (generics, kind, has_default) = match &i.kind {
-            AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
+        let (ident, generics, kind, has_default) = match &i.kind {
+            AssocItemKind::Const(box ConstItem {
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+                ..
+            }) => {
                 let (generics, kind) = self.lower_generics(
                     generics,
                     i.id,
@@ -766,10 +814,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         hir::TraitItemKind::Const(ty, body)
                     },
                 );
-                (generics, kind, expr.is_some())
+
+                if define_opaque.is_some() {
+                    if expr.is_some() {
+                        self.lower_define_opaque(hir_id, &define_opaque);
+                    } else {
+                        self.dcx().span_err(
+                            i.span,
+                            "only trait consts with default bodies can define opaque types",
+                        );
+                    }
+                }
+
+                (*ident, generics, kind, expr.is_some())
             }
-            AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
-                let names = self.lower_fn_params_to_names(&sig.decl);
+            AssocItemKind::Fn(box Fn {
+                sig, ident, generics, body: None, define_opaque, ..
+            }) => {
+                // FIXME(contracts): Deny contract here since it won't apply to
+                // any impl method or callees.
+                let idents = self.lower_fn_params_to_idents(&sig.decl);
                 let (generics, sig) = self.lower_method_sig(
                     generics,
                     sig,
@@ -778,9 +842,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     sig.header.coroutine_kind,
                     attrs,
                 );
-                (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
+                if define_opaque.is_some() {
+                    self.dcx().span_err(
+                        i.span,
+                        "only trait methods with default bodies can define opaque types",
+                    );
+                }
+                (
+                    *ident,
+                    generics,
+                    hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(idents)),
+                    false,
+                )
             }
-            AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
+            AssocItemKind::Fn(box Fn {
+                sig,
+                ident,
+                generics,
+                body: Some(body),
+                contract,
+                define_opaque,
+                ..
+            }) => {
                 let body_id = self.lower_maybe_coroutine_body(
                     sig.span,
                     i.span,
@@ -789,6 +872,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     sig.header.coroutine_kind,
                     Some(body),
                     attrs,
+                    contract.as_deref(),
                 );
                 let (generics, sig) = self.lower_method_sig(
                     generics,
@@ -798,9 +882,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     sig.header.coroutine_kind,
                     attrs,
                 );
-                (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
+                self.lower_define_opaque(hir_id, &define_opaque);
+                (
+                    *ident,
+                    generics,
+                    hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)),
+                    true,
+                )
             }
-            AssocItemKind::Type(box TyAlias { generics, where_clauses, bounds, ty, .. }) => {
+            AssocItemKind::Type(box TyAlias {
+                ident, generics, where_clauses, bounds, ty, ..
+            }) => {
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, *where_clauses, false);
                 let (generics, kind) = self.lower_generics(
@@ -823,15 +915,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         )
                     },
                 );
-                (generics, kind, ty.is_some())
+                (*ident, generics, kind, ty.is_some())
             }
             AssocItemKind::Delegation(box delegation) => {
-                let delegation_results = self.lower_delegation(delegation, i.id);
+                let delegation_results = self.lower_delegation(delegation, i.id, false);
                 let item_kind = hir::TraitItemKind::Fn(
                     delegation_results.sig,
                     hir::TraitFn::Provided(delegation_results.body_id),
                 );
-                (delegation_results.generics, item_kind, true)
+                (delegation.ident, delegation_results.generics, item_kind, true)
             }
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                 panic!("macros should have been expanded by now")
@@ -840,7 +932,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let item = hir::TraitItem {
             owner_id: trait_item_def_id,
-            ident: self.lower_ident(i.ident),
+            ident: self.lower_ident(ident),
             generics,
             kind,
             span: self.lower_span(i.span),
@@ -850,23 +942,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_trait_item_ref(&mut self, i: &AssocItem) -> hir::TraitItemRef {
-        let kind = match &i.kind {
-            AssocItemKind::Const(..) => hir::AssocItemKind::Const,
-            AssocItemKind::Type(..) => hir::AssocItemKind::Type,
-            AssocItemKind::Fn(box Fn { sig, .. }) => {
-                hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
+        let (ident, kind) = match &i.kind {
+            AssocItemKind::Const(box ConstItem { ident, .. }) => {
+                (*ident, hir::AssocItemKind::Const)
             }
-            AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
-                has_self: self.delegation_has_self(i.id, delegation.id, i.span),
-            },
+            AssocItemKind::Type(box TyAlias { ident, .. }) => (*ident, hir::AssocItemKind::Type),
+            AssocItemKind::Fn(box Fn { ident, sig, .. }) => {
+                (*ident, hir::AssocItemKind::Fn { has_self: sig.decl.has_self() })
+            }
+            AssocItemKind::Delegation(box delegation) => (
+                delegation.ident,
+                hir::AssocItemKind::Fn {
+                    has_self: self.delegatee_is_method(i.id, delegation.id, i.span, false),
+                },
+            ),
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                 panic!("macros should have been expanded by now")
             }
         };
-        let id = hir::TraitItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } };
+        let id = hir::TraitItemId { owner_id: self.owner_id(i.id) };
         hir::TraitItemRef {
             id,
-            ident: self.lower_ident(i.ident),
+            ident: self.lower_ident(ident),
             span: self.lower_span(i.span),
             kind,
         }
@@ -877,27 +974,49 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.expr(span, hir::ExprKind::Err(guar))
     }
 
-    fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
+    fn lower_impl_item(
+        &mut self,
+        i: &AssocItem,
+        is_in_trait_impl: bool,
+    ) -> &'hir hir::ImplItem<'hir> {
         // Since `default impl` is not yet implemented, this is always true in impls.
         let has_value = true;
         let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
         let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
-        let attrs = self.lower_attrs(hir_id, &i.attrs);
+        let attrs = self.lower_attrs(hir_id, &i.attrs, i.span);
 
-        let (generics, kind) = match &i.kind {
-            AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
+        let (ident, (generics, kind)) = match &i.kind {
+            AssocItemKind::Const(box ConstItem {
+                ident,
                 generics,
-                i.id,
-                ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                |this| {
-                    let ty =
-                        this.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
-                    let body = this.lower_const_body(i.span, expr.as_deref());
-
-                    hir::ImplItemKind::Const(ty, body)
-                },
+                ty,
+                expr,
+                define_opaque,
+                ..
+            }) => (
+                *ident,
+                self.lower_generics(
+                    generics,
+                    i.id,
+                    ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                    |this| {
+                        let ty = this
+                            .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
+                        let body = this.lower_const_body(i.span, expr.as_deref());
+                        this.lower_define_opaque(hir_id, &define_opaque);
+                        hir::ImplItemKind::Const(ty, body)
+                    },
+                ),
             ),
-            AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
+            AssocItemKind::Fn(box Fn {
+                sig,
+                ident,
+                generics,
+                body,
+                contract,
+                define_opaque,
+                ..
+            }) => {
                 let body_id = self.lower_maybe_coroutine_body(
                     sig.span,
                     i.span,
@@ -906,51 +1025,62 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     sig.header.coroutine_kind,
                     body.as_deref(),
                     attrs,
+                    contract.as_deref(),
                 );
                 let (generics, sig) = self.lower_method_sig(
                     generics,
                     sig,
                     i.id,
-                    if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
+                    if is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
                     sig.header.coroutine_kind,
                     attrs,
                 );
+                self.lower_define_opaque(hir_id, &define_opaque);
 
-                (generics, hir::ImplItemKind::Fn(sig, body_id))
+                (*ident, (generics, hir::ImplItemKind::Fn(sig, body_id)))
             }
-            AssocItemKind::Type(box TyAlias { generics, where_clauses, ty, .. }) => {
+            AssocItemKind::Type(box TyAlias { ident, generics, where_clauses, ty, .. }) => {
                 let mut generics = generics.clone();
                 add_ty_alias_where_clause(&mut generics, *where_clauses, false);
-                self.lower_generics(
-                    &generics,
-                    i.id,
-                    ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                    |this| match ty {
-                        None => {
-                            let guar = this.dcx().span_delayed_bug(
-                                i.span,
-                                "expected to lower associated type, but it was missing",
-                            );
-                            let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err(guar)));
-                            hir::ImplItemKind::Type(ty)
-                        }
-                        Some(ty) => {
-                            let ty = this.lower_ty(ty, ImplTraitContext::OpaqueTy {
-                                origin: hir::OpaqueTyOrigin::TyAlias {
-                                    parent: this.local_def_id(i.id),
-                                    in_assoc_ty: true,
-                                },
-                            });
-                            hir::ImplItemKind::Type(ty)
-                        }
-                    },
+                (
+                    *ident,
+                    self.lower_generics(
+                        &generics,
+                        i.id,
+                        ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                        |this| match ty {
+                            None => {
+                                let guar = this.dcx().span_delayed_bug(
+                                    i.span,
+                                    "expected to lower associated type, but it was missing",
+                                );
+                                let ty = this.arena.alloc(this.ty(i.span, hir::TyKind::Err(guar)));
+                                hir::ImplItemKind::Type(ty)
+                            }
+                            Some(ty) => {
+                                let ty = this.lower_ty(
+                                    ty,
+                                    ImplTraitContext::OpaqueTy {
+                                        origin: hir::OpaqueTyOrigin::TyAlias {
+                                            parent: this.local_def_id(i.id),
+                                            in_assoc_ty: true,
+                                        },
+                                    },
+                                );
+                                hir::ImplItemKind::Type(ty)
+                            }
+                        },
+                    ),
                 )
             }
             AssocItemKind::Delegation(box delegation) => {
-                let delegation_results = self.lower_delegation(delegation, i.id);
+                let delegation_results = self.lower_delegation(delegation, i.id, is_in_trait_impl);
                 (
-                    delegation_results.generics,
-                    hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id),
+                    delegation.ident,
+                    (
+                        delegation_results.generics,
+                        hir::ImplItemKind::Fn(delegation_results.sig, delegation_results.body_id),
+                    ),
                 )
             }
             AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
@@ -960,7 +1090,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let item = hir::ImplItem {
             owner_id: hir_id.expect_owner(),
-            ident: self.lower_ident(i.ident),
+            ident: self.lower_ident(ident),
             generics,
             kind,
             vis_span: self.lower_span(i.vis.span),
@@ -970,10 +1100,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.arena.alloc(item)
     }
 
-    fn lower_impl_item_ref(&mut self, i: &AssocItem) -> hir::ImplItemRef {
+    fn lower_impl_item_ref(&mut self, i: &AssocItem, is_in_trait_impl: bool) -> hir::ImplItemRef {
         hir::ImplItemRef {
-            id: hir::ImplItemId { owner_id: hir::OwnerId { def_id: self.local_def_id(i.id) } },
-            ident: self.lower_ident(i.ident),
+            id: hir::ImplItemId { owner_id: self.owner_id(i.id) },
+            // `unwrap` is safe because `AssocItemKind::{MacCall,DelegationMac}` are the only
+            // assoc item kinds without an identifier and they cannot reach here.
+            ident: self.lower_ident(i.kind.ident().unwrap()),
             span: self.lower_span(i.span),
             kind: match &i.kind {
                 AssocItemKind::Const(..) => hir::AssocItemKind::Const,
@@ -982,7 +1114,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     hir::AssocItemKind::Fn { has_self: sig.decl.has_self() }
                 }
                 AssocItemKind::Delegation(box delegation) => hir::AssocItemKind::Fn {
-                    has_self: self.delegation_has_self(i.id, delegation.id, i.span),
+                    has_self: self.delegatee_is_method(
+                        i.id,
+                        delegation.id,
+                        i.span,
+                        is_in_trait_impl,
+                    ),
                 },
                 AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
                     panic!("macros should have been expanded by now")
@@ -1039,7 +1176,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> {
         let hir_id = self.lower_node_id(param.id);
-        self.lower_attrs(hir_id, &param.attrs);
+        self.lower_attrs(hir_id, &param.attrs, param.span);
         hir::Param {
             hir_id,
             pat: self.lower_pat(&param.pat),
@@ -1051,26 +1188,107 @@ impl<'hir> LoweringContext<'_, 'hir> {
     pub(super) fn lower_fn_body(
         &mut self,
         decl: &FnDecl,
+        contract: Option<&FnContract>,
         body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
     ) -> hir::BodyId {
         self.lower_body(|this| {
-            (
-                this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))),
-                body(this),
-            )
+            let params =
+                this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
+
+            // Optionally lower the fn contract, which turns:
+            //
+            // { body }
+            //
+            // into:
+            //
+            // { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) }
+            if let Some(contract) = contract {
+                let precond = if let Some(req) = &contract.requires {
+                    // Lower the precondition check intrinsic.
+                    let lowered_req = this.lower_expr_mut(&req);
+                    let req_span = this.mark_span_with_reason(
+                        DesugaringKind::Contract,
+                        lowered_req.span,
+                        None,
+                    );
+                    let precond = this.expr_call_lang_item_fn_mut(
+                        req_span,
+                        hir::LangItem::ContractCheckRequires,
+                        &*arena_vec![this; lowered_req],
+                    );
+                    Some(this.stmt_expr(req.span, precond))
+                } else {
+                    None
+                };
+                let (postcond, body) = if let Some(ens) = &contract.ensures {
+                    let ens_span = this.lower_span(ens.span);
+                    let ens_span =
+                        this.mark_span_with_reason(DesugaringKind::Contract, ens_span, None);
+                    // Set up the postcondition `let` statement.
+                    let check_ident: Ident =
+                        Ident::from_str_and_span("__ensures_checker", ens_span);
+                    let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut(
+                        ens_span,
+                        check_ident,
+                        hir::BindingMode::NONE,
+                    );
+                    let lowered_ens = this.lower_expr_mut(&ens);
+                    let postcond_checker = this.expr_call_lang_item_fn(
+                        ens_span,
+                        hir::LangItem::ContractBuildCheckEnsures,
+                        &*arena_vec![this; lowered_ens],
+                    );
+                    let postcond = this.stmt_let_pat(
+                        None,
+                        ens_span,
+                        Some(postcond_checker),
+                        this.arena.alloc(checker_pat),
+                        hir::LocalSource::Contract,
+                    );
+
+                    // Install contract_ensures so we will intercept `return` statements,
+                    // then lower the body.
+                    this.contract_ensures = Some((ens_span, check_ident, check_hir_id));
+                    let body = this.arena.alloc(body(this));
+
+                    // Finally, inject an ensures check on the implicit return of the body.
+                    let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id);
+                    (Some(postcond), body)
+                } else {
+                    let body = &*this.arena.alloc(body(this));
+                    (None, body)
+                };
+                // Flatten the body into precond, then postcond, then wrapped body.
+                let wrapped_body = this.block_all(
+                    body.span,
+                    this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()),
+                    Some(body),
+                );
+                (params, this.expr_block(wrapped_body))
+            } else {
+                (params, body(this))
+            }
         })
     }
 
-    fn lower_fn_body_block(&mut self, decl: &FnDecl, body: &Block) -> hir::BodyId {
-        self.lower_fn_body(decl, |this| this.lower_block_expr(body))
+    fn lower_fn_body_block(
+        &mut self,
+        decl: &FnDecl,
+        body: &Block,
+        contract: Option<&FnContract>,
+    ) -> hir::BodyId {
+        self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body))
     }
 
     pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
         self.lower_body(|this| {
-            (&[], match expr {
-                Some(expr) => this.lower_expr_mut(expr),
-                None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
-            })
+            (
+                &[],
+                match expr {
+                    Some(expr) => this.lower_expr_mut(expr),
+                    None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")),
+                },
+            )
         })
     }
 
@@ -1085,13 +1303,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
         coroutine_kind: Option<CoroutineKind>,
         body: Option<&Block>,
         attrs: &'hir [hir::Attribute],
+        contract: Option<&FnContract>,
     ) -> hir::BodyId {
         let Some(body) = body else {
             // Functions without a body are an error, except if this is an intrinsic. For those we
             // create a fake body so that the entire rest of the compiler doesn't have to deal with
             // this as a special case.
-            return self.lower_fn_body(decl, |this| {
+            return self.lower_fn_body(decl, contract, |this| {
                 if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) {
+                    let span = this.lower_span(span);
                     let empty_block = hir::Block {
                         hir_id: this.next_id(),
                         stmts: &[],
@@ -1114,8 +1334,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         };
         let Some(coroutine_kind) = coroutine_kind else {
             // Typical case: not a coroutine.
-            return self.lower_fn_body_block(decl, body);
+            return self.lower_fn_body_block(decl, body, contract);
         };
+        // FIXME(contracts): Support contracts on async fn.
         self.lower_body(|this| {
             let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
                 decl,
@@ -1384,23 +1605,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    pub(super) fn lower_abi(&mut self, abi: StrLit) -> abi::Abi {
-        abi::lookup(abi.symbol_unescaped.as_str()).unwrap_or_else(|err| {
-            self.error_on_invalid_abi(abi, err);
-            abi::Abi::Rust
-        })
+    pub(super) fn lower_abi(&mut self, abi_str: StrLit) -> ExternAbi {
+        let ast::StrLit { symbol_unescaped, span, .. } = abi_str;
+        let extern_abi = symbol_unescaped.as_str().parse().unwrap_or_else(|_| {
+            self.error_on_invalid_abi(abi_str);
+            ExternAbi::Rust
+        });
+        let sess = self.tcx.sess;
+        let features = self.tcx.features();
+        gate_unstable_abi(sess, features, span, extern_abi);
+        extern_abi
     }
 
-    pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi {
+    pub(super) fn lower_extern(&mut self, ext: Extern) -> ExternAbi {
         match ext {
-            Extern::None => abi::Abi::Rust,
-            Extern::Implicit(_) => abi::Abi::FALLBACK,
+            Extern::None => ExternAbi::Rust,
+            Extern::Implicit(_) => ExternAbi::FALLBACK,
             Extern::Explicit(abi, _) => self.lower_abi(abi),
         }
     }
 
-    fn error_on_invalid_abi(&self, abi: StrLit, err: abi::AbiUnsupported) {
-        let abi_names = abi::enabled_names(self.tcx.features(), abi.span)
+    fn error_on_invalid_abi(&self, abi: StrLit) {
+        let abi_names = enabled_names(self.tcx.features(), abi.span)
             .iter()
             .map(|s| Symbol::intern(s))
             .collect::<Vec<_>>();
@@ -1408,13 +1634,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.dcx().emit_err(InvalidAbi {
             abi: abi.symbol_unescaped,
             span: abi.span,
-            explain: match err {
-                abi::AbiUnsupported::Reason { explain } => Some(InvalidAbiReason(explain)),
-                _ => None,
-            },
             suggestion: suggested_name.map(|suggested_name| InvalidAbiSuggestion {
                 span: abi.span,
-                suggestion: format!("\"{suggested_name}\""),
+                suggestion: suggested_name.to_string(),
             }),
             command: "rustc --print=calling-conventions".to_string(),
         });
@@ -1561,6 +1783,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
         (lowered_generics, res)
     }
 
+    pub(super) fn lower_define_opaque(
+        &mut self,
+        hir_id: HirId,
+        define_opaque: &Option<ThinVec<(NodeId, Path)>>,
+    ) {
+        assert_eq!(self.define_opaque, None);
+        assert!(hir_id.is_owner());
+        let Some(define_opaque) = define_opaque.as_ref() else {
+            return;
+        };
+        let define_opaque = define_opaque.iter().filter_map(|(id, path)| {
+            let res = self.resolver.get_partial_res(*id);
+            let Some(did) = res.and_then(|res| res.expect_full_res().opt_def_id()) else {
+                self.dcx().span_delayed_bug(path.span, "should have errored in resolve");
+                return None;
+            };
+            let Some(did) = did.as_local() else {
+                self.dcx().span_err(
+                    path.span,
+                    "only opaque types defined in the local crate can be defined",
+                );
+                return None;
+            };
+            Some((self.lower_span(path.span), did))
+        });
+        let define_opaque = self.arena.alloc_from_iter(define_opaque);
+        self.define_opaque = Some(define_opaque);
+    }
+
     pub(super) fn lower_generic_bound_predicate(
         &mut self,
         ident: Ident,
@@ -1579,7 +1830,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
         let bounds = self.lower_param_bounds(bounds, itctx);
 
-        let ident = self.lower_ident(ident);
         let param_span = ident.span;
 
         // Reconstruct the span of the entire predicate from the individual generic bounds.
@@ -1598,6 +1848,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let def_id = self.local_def_id(id).to_def_id();
                 let hir_id = self.next_id();
                 let res = Res::Def(DefKind::TyParam, def_id);
+                let ident = self.lower_ident(ident);
                 let ty_path = self.arena.alloc(hir::Path {
                     span: param_span,
                     res,
@@ -1616,9 +1867,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 })
             }
             GenericParamKind::Lifetime => {
-                let ident = self.lower_ident(ident);
                 let lt_id = self.next_node_id();
-                let lifetime = self.new_named_lifetime(id, lt_id, ident);
+                let lifetime = self.new_named_lifetime(id, lt_id, ident, IsAnonInPath::No);
                 hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
                     lifetime,
                     bounds,
@@ -1632,6 +1882,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
         let hir_id = self.lower_node_id(pred.id);
         let span = self.lower_span(pred.span);
+        self.lower_attrs(hir_id, &pred.attrs, span);
         let kind = self.arena.alloc(match &pred.kind {
             WherePredicateKind::BoundPredicate(WhereBoundPredicate {
                 bound_generic_params,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0e28590bd660..c40987541f4a 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -35,31 +35,35 @@
 #![doc(rust_logo)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
+#![feature(exact_size_is_empty)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
+use std::sync::Arc;
+
 use rustc_ast::node_id::NodeMap;
 use rustc_ast::{self as ast, *};
-use rustc_data_structures::captures::Captures;
+use rustc_attr_parsing::{AttributeParser, OmitDoc};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::tagged_ptr::TaggedRef;
 use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
 use rustc_hir::{
-    self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, ParamName, TraitCandidate,
+    self as hir, ConstArg, GenericArg, HirId, IsAnonInPath, ItemLocalMap, LangItem, ParamName,
+    TraitCandidate,
 };
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_macros::extension;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
 use rustc_session::parse::{add_feature_diagnostics, feature_err};
-use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
+use rustc_span::symbol::{Ident, Symbol, kw, sym};
+use rustc_span::{DUMMY_SP, DesugaringKind, Span};
 use smallvec::{SmallVec, smallvec};
 use thin_vec::ThinVec;
 use tracing::{debug, instrument, trace};
@@ -82,6 +86,7 @@ mod index;
 mod item;
 mod pat;
 mod path;
+pub mod stability;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
@@ -94,11 +99,15 @@ struct LoweringContext<'a, 'hir> {
 
     /// Bodies inside the owner being lowered.
     bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
+    /// `#[define_opaque]` attributes
+    define_opaque: Option<&'hir [(Span, LocalDefId)]>,
     /// Attributes inside the owner being lowered.
     attrs: SortedMap<hir::ItemLocalId, &'hir [hir::Attribute]>,
     /// Collect items that were created by lowering the current owner.
     children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>,
 
+    contract_ensures: Option<(Span, Ident, HirId)>,
+
     coroutine_kind: Option<hir::CoroutineKind>,
 
     /// When inside an `async` context, this is the `HirId` of the
@@ -112,7 +121,6 @@ struct LoweringContext<'a, 'hir> {
     catch_scope: Option<HirId>,
     loop_scope: Option<HirId>,
     is_in_loop_condition: bool,
-    is_in_trait_impl: bool,
     is_in_dyn_type: bool,
 
     current_hir_id_owner: hir::OwnerId,
@@ -128,15 +136,19 @@ struct LoweringContext<'a, 'hir> {
     #[cfg(debug_assertions)]
     node_id_to_local_id: NodeMap<hir::ItemLocalId>,
 
-    allow_try_trait: Lrc<[Symbol]>,
-    allow_gen_future: Lrc<[Symbol]>,
-    allow_async_iterator: Lrc<[Symbol]>,
-    allow_for_await: Lrc<[Symbol]>,
-    allow_async_fn_traits: Lrc<[Symbol]>,
+    allow_try_trait: Arc<[Symbol]>,
+    allow_gen_future: Arc<[Symbol]>,
+    allow_pattern_type: Arc<[Symbol]>,
+    allow_async_iterator: Arc<[Symbol]>,
+    allow_for_await: Arc<[Symbol]>,
+    allow_async_fn_traits: Arc<[Symbol]>,
+
+    attribute_parser: AttributeParser<'hir>,
 }
 
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
+        let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect();
         Self {
             // Pseudo-globals.
             tcx,
@@ -145,8 +157,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
             // HirId handling.
             bodies: Vec::new(),
+            define_opaque: None,
             attrs: SortedMap::default(),
             children: Vec::default(),
+            contract_ensures: None,
             current_hir_id_owner: hir::CRATE_OWNER_ID,
             item_local_id_counter: hir::ItemLocalId::ZERO,
             ident_and_label_to_local_id: Default::default(),
@@ -158,7 +172,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             catch_scope: None,
             loop_scope: None,
             is_in_loop_condition: false,
-            is_in_trait_impl: false,
             is_in_dyn_type: false,
             coroutine_kind: None,
             task_context: None,
@@ -166,6 +179,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             impl_trait_defs: Vec::new(),
             impl_trait_bounds: Vec::new(),
             allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
+            allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(),
             allow_gen_future: if tcx.features().async_fn_track_caller() {
                 [sym::gen_future, sym::closure_track_caller].into()
             } else {
@@ -176,6 +190,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller`
             // interact with `gen`/`async gen` blocks
             allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
+
+            attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools),
         }
     }
 
@@ -211,7 +227,6 @@ impl ResolverAstLowering {
         None
     }
 
-    /// Obtains resolution for a `NodeId` with a single resolution.
     fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
         self.partial_res_map.get(&id).copied()
     }
@@ -401,7 +416,7 @@ fn compute_hir_hash(
         .iter_enumerated()
         .filter_map(|(def_id, info)| {
             let info = info.as_owner()?;
-            let def_path_hash = tcx.hir().def_path_hash(def_id);
+            let def_path_hash = tcx.hir_def_path_hash(def_id);
             Some((def_path_hash, info))
         })
         .collect();
@@ -417,10 +432,10 @@ fn compute_hir_hash(
 pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
     let sess = tcx.sess;
     // Queries that borrow `resolver_for_lowering`.
-    tcx.ensure_with_value().output_filenames(());
-    tcx.ensure_with_value().early_lint_checks(());
-    tcx.ensure_with_value().debugger_visualizers(LOCAL_CRATE);
-    tcx.ensure_with_value().get_lang_items(());
+    tcx.ensure_done().output_filenames(());
+    tcx.ensure_done().early_lint_checks(());
+    tcx.ensure_done().debugger_visualizers(LOCAL_CRATE);
+    tcx.ensure_done().get_lang_items(());
     let (mut resolver, krate) = tcx.resolver_for_lowering().steal();
 
     let ast_index = index_crate(&resolver.node_id_to_def_id, &krate);
@@ -481,7 +496,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         parent: LocalDefId,
         node_id: ast::NodeId,
-        name: Symbol,
+        name: Option<Symbol>,
         def_kind: DefKind,
         span: Span,
     ) -> LocalDefId {
@@ -491,7 +506,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             "adding a def'n for node-id {:?} and def kind {:?} but a previous def'n exists: {:?}",
             node_id,
             def_kind,
-            self.tcx.hir().def_key(self.local_def_id(node_id)),
+            self.tcx.hir_def_key(self.local_def_id(node_id)),
         );
 
         let def_id = self.tcx.at(span).create_def(parent, name, def_kind).def_id();
@@ -519,6 +534,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`"))
     }
 
+    /// Given the id of an owner node in the AST, returns the corresponding `OwnerId`.
+    fn owner_id(&self, node: NodeId) -> hir::OwnerId {
+        hir::OwnerId { def_id: self.local_def_id(node) }
+    }
+
     /// Freshen the `LoweringContext` and ready it to lower a nested item.
     /// The lowered item is registered into `self.children`.
     ///
@@ -530,18 +550,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         owner: NodeId,
         f: impl FnOnce(&mut Self) -> hir::OwnerNode<'hir>,
     ) {
-        let def_id = self.local_def_id(owner);
+        let owner_id = self.owner_id(owner);
 
         let current_attrs = std::mem::take(&mut self.attrs);
         let current_bodies = std::mem::take(&mut self.bodies);
+        let current_define_opaque = std::mem::take(&mut self.define_opaque);
         let current_ident_and_label_to_local_id =
             std::mem::take(&mut self.ident_and_label_to_local_id);
 
         #[cfg(debug_assertions)]
         let current_node_id_to_local_id = std::mem::take(&mut self.node_id_to_local_id);
         let current_trait_map = std::mem::take(&mut self.trait_map);
-        let current_owner =
-            std::mem::replace(&mut self.current_hir_id_owner, hir::OwnerId { def_id });
+        let current_owner = std::mem::replace(&mut self.current_hir_id_owner, owner_id);
         let current_local_counter =
             std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1));
         let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs);
@@ -559,7 +579,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
 
         let item = f(self);
-        debug_assert_eq!(def_id, item.def_id().def_id);
+        debug_assert_eq!(owner_id, item.def_id());
         // `f` should have consumed all the elements in these vectors when constructing `item`.
         debug_assert!(self.impl_trait_defs.is_empty());
         debug_assert!(self.impl_trait_bounds.is_empty());
@@ -567,6 +587,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         self.attrs = current_attrs;
         self.bodies = current_bodies;
+        self.define_opaque = current_define_opaque;
         self.ident_and_label_to_local_id = current_ident_and_label_to_local_id;
 
         #[cfg(debug_assertions)]
@@ -579,13 +600,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         self.impl_trait_defs = current_impl_trait_defs;
         self.impl_trait_bounds = current_impl_trait_bounds;
 
-        debug_assert!(!self.children.iter().any(|(id, _)| id == &def_id));
-        self.children.push((def_id, hir::MaybeOwner::Owner(info)));
+        debug_assert!(!self.children.iter().any(|(id, _)| id == &owner_id.def_id));
+        self.children.push((owner_id.def_id, hir::MaybeOwner::Owner(info)));
     }
 
     fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
         let attrs = std::mem::take(&mut self.attrs);
         let mut bodies = std::mem::take(&mut self.bodies);
+        let define_opaque = std::mem::take(&mut self.define_opaque);
         let trait_map = std::mem::take(&mut self.trait_map);
 
         #[cfg(debug_assertions)]
@@ -601,11 +623,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         // Don't hash unless necessary, because it's expensive.
         let (opt_hash_including_bodies, attrs_hash) =
-            self.tcx.hash_owner_nodes(node, &bodies, &attrs);
+            self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque);
         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 };
-        let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash };
+        let attrs = hir::AttributeMap { map: attrs, opt_hash: attrs_hash, define_opaque };
 
         self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
     }
@@ -721,7 +743,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &self,
         reason: DesugaringKind,
         span: Span,
-        allow_internal_unstable: Option<Lrc<[Symbol]>>,
+        allow_internal_unstable: Option<Arc<[Symbol]>>,
     ) -> Span {
         self.tcx.with_stable_hashing_context(|hcx| {
             span.mark_with_reason(allow_internal_unstable, reason, span.edition(), hcx)
@@ -761,7 +783,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let _def_id = self.create_def(
                     self.current_hir_id_owner.def_id,
                     param,
-                    kw::UnderscoreLifetime,
+                    Some(kw::UnderscoreLifetime),
                     DefKind::LifetimeParam,
                     ident.span,
                 );
@@ -833,12 +855,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let was_in_loop_condition = self.is_in_loop_condition;
         self.is_in_loop_condition = false;
 
+        let old_contract = self.contract_ensures.take();
+
         let catch_scope = self.catch_scope.take();
         let loop_scope = self.loop_scope.take();
         let ret = f(self);
         self.catch_scope = catch_scope;
         self.loop_scope = loop_scope;
 
+        self.contract_ensures = old_contract;
+
         self.is_in_loop_condition = was_in_loop_condition;
 
         self.current_item = current_item;
@@ -846,45 +872,38 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         ret
     }
 
-    fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [hir::Attribute] {
+    fn lower_attrs(
+        &mut self,
+        id: HirId,
+        attrs: &[Attribute],
+        target_span: Span,
+    ) -> &'hir [hir::Attribute] {
         if attrs.is_empty() {
             &[]
         } else {
+            let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span));
+
             debug_assert_eq!(id.owner, self.current_hir_id_owner);
-            let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a)));
-            debug_assert!(!ret.is_empty());
-            self.attrs.insert(id.local_id, ret);
-            ret
+            let ret = self.arena.alloc_from_iter(lowered_attrs);
+
+            // this is possible if an item contained syntactical attribute,
+            // but none of them parse succesfully or all of them were ignored
+            // for not being built-in attributes at all. They could be remaining
+            // unexpanded attributes used as markers in proc-macro derives for example.
+            // This will have emitted some diagnostics for the misparse, but will then
+            // not emit the attribute making the list empty.
+            if ret.is_empty() {
+                &[]
+            } else {
+                self.attrs.insert(id.local_id, ret);
+                ret
+            }
         }
     }
 
-    fn lower_attr(&self, attr: &Attribute) -> hir::Attribute {
-        // Note that we explicitly do not walk the path. Since we don't really
-        // lower attributes (we use the AST version) there is nowhere to keep
-        // the `HirId`s. We don't actually need HIR version of attributes anyway.
-        // Tokens are also not needed after macro expansion and parsing.
-        let kind = match attr.kind {
-            AttrKind::Normal(ref normal) => hir::AttrKind::Normal(Box::new(hir::AttrItem {
-                unsafety: self.lower_safety(normal.item.unsafety, hir::Safety::Safe),
-                path: hir::AttrPath {
-                    segments: normal
-                        .item
-                        .path
-                        .segments
-                        .iter()
-                        .map(|i| i.ident)
-                        .collect::<Vec<_>>()
-                        .into_boxed_slice(),
-                    span: normal.item.path.span,
-                },
-                args: self.lower_attr_args(&normal.item.args),
-            })),
-            AttrKind::DocComment(comment_kind, data) => {
-                hir::AttrKind::DocComment(comment_kind, data)
-            }
-        };
-
-        hir::Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) }
+    fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec<hir::Attribute> {
+        self.attribute_parser
+            .parse_attribute_list(attrs, target_span, OmitDoc::Lower, |s| self.lower_span(s))
     }
 
     fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
@@ -896,36 +915,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
-    fn lower_attr_args(&self, args: &AttrArgs) -> hir::AttrArgs {
-        match args {
-            AttrArgs::Empty => hir::AttrArgs::Empty,
-            AttrArgs::Delimited(args) => hir::AttrArgs::Delimited(self.lower_delim_args(args)),
-            // This is an inert key-value attribute - it will never be visible to macros
-            // after it gets lowered to HIR. Therefore, we can extract literals to handle
-            // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
-            &AttrArgs::Eq { eq_span, ref expr } => {
-                // In valid code the value always ends up as a single literal. Otherwise, a dummy
-                // literal suffices because the error is handled elsewhere.
-                let lit = if let ExprKind::Lit(token_lit) = expr.kind
-                    && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
-                {
-                    lit
-                } else {
-                    let guar = self.dcx().has_errors().unwrap();
-                    MetaItemLit {
-                        symbol: kw::Empty,
-                        suffix: None,
-                        kind: LitKind::Err(guar),
-                        span: DUMMY_SP,
-                    }
-                };
-                hir::AttrArgs::Eq { eq_span, expr: lit }
-            }
-        }
-    }
-
     fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs {
-        DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() }
+        DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.clone() }
     }
 
     /// Lower an associated item constraint.
@@ -946,19 +937,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     if let Some(first_char) = constraint.ident.as_str().chars().next()
                         && first_char.is_ascii_lowercase()
                     {
-                        let mut err = if !data.inputs.is_empty() {
-                            self.dcx().create_err(errors::BadReturnTypeNotation::Inputs {
-                                span: data.inputs_span,
-                            })
-                        } else if let FnRetTy::Ty(ty) = &data.output {
-                            self.dcx().create_err(errors::BadReturnTypeNotation::Output {
-                                span: data.inputs_span.shrink_to_hi().to(ty.span),
-                            })
-                        } else {
-                            self.dcx().create_err(errors::BadReturnTypeNotation::NeedsDots {
-                                span: data.inputs_span,
-                            })
+                        let err = match (&data.inputs[..], &data.output) {
+                            ([_, ..], FnRetTy::Default(_)) => {
+                                errors::BadReturnTypeNotation::Inputs { span: data.inputs_span }
+                            }
+                            ([], FnRetTy::Default(_)) => {
+                                errors::BadReturnTypeNotation::NeedsDots { span: data.inputs_span }
+                            }
+                            // The case `T: Trait<method(..) -> Ret>` is handled in the parser.
+                            (_, FnRetTy::Ty(ty)) => {
+                                let span = data.inputs_span.shrink_to_hi().to(ty.span);
+                                errors::BadReturnTypeNotation::Output {
+                                    span,
+                                    suggestion: errors::RTNSuggestion {
+                                        output: span,
+                                        input: data.inputs_span,
+                                    },
+                                }
+                            }
                         };
+                        let mut err = self.dcx().create_err(err);
                         if !self.tcx.features().return_type_notation()
                             && self.tcx.sess.is_nightly_build()
                         {
@@ -1083,17 +1081,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         match arg {
             ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)),
             ast::GenericArg::Type(ty) => {
+                // We cannot just match on `TyKind::Infer` as `(_)` is represented as
+                // `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer`
+                if ty.is_maybe_parenthesised_infer() {
+                    return GenericArg::Infer(hir::InferArg {
+                        hir_id: self.lower_node_id(ty.id),
+                        span: self.lower_span(ty.span),
+                    });
+                }
+
                 match &ty.kind {
-                    TyKind::Infer if self.tcx.features().generic_arg_infer() => {
-                        return GenericArg::Infer(hir::InferArg {
-                            hir_id: self.lower_node_id(ty.id),
-                            span: self.lower_span(ty.span),
-                        });
-                    }
                     // We parse const arguments as path types as we cannot distinguish them during
                     // parsing. We try to resolve that ambiguity by attempting resolution in both the
                     // type and value namespaces. If we resolved the path in the value namespace, we
                     // transform it into a generic const argument.
+                    //
+                    // FIXME: Should we be handling `(PATH_TO_CONST)`?
                     TyKind::Path(None, path) => {
                         if let Some(res) = self
                             .resolver
@@ -1101,7 +1104,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             .and_then(|partial_res| partial_res.full_res())
                         {
                             if !res.matches_ns(Namespace::TypeNS)
-                                && path.is_potential_trivial_const_arg()
+                                && path.is_potential_trivial_const_arg(false)
                             {
                                 debug!(
                                     "lower_generic_arg: Lowering type argument as const argument: {:?}",
@@ -1110,15 +1113,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                                 let ct =
                                     self.lower_const_path_to_const_arg(path, res, ty.id, ty.span);
-                                return GenericArg::Const(ct);
+                                return GenericArg::Const(ct.try_as_ambig_ct().unwrap());
                             }
                         }
                     }
                     _ => {}
                 }
-                GenericArg::Type(self.lower_ty(ty, itctx))
+                GenericArg::Type(self.lower_ty(ty, itctx).try_as_ambig_ty().unwrap())
+            }
+            ast::GenericArg::Const(ct) => {
+                GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap())
             }
-            ast::GenericArg::Const(ct) => GenericArg::Const(self.lower_anon_const_to_const_arg(ct)),
         }
     }
 
@@ -1158,7 +1163,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let lifetime_bound = this.elided_dyn_bound(t.span);
                 (bounds, lifetime_bound)
             });
-            let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None);
+            let kind = hir::TyKind::TraitObject(
+                bounds,
+                TaggedRef::new(lifetime_bound, TraitObjectSyntax::None),
+            );
             return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() };
         }
 
@@ -1185,7 +1193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
         let kind = match &t.kind {
-            TyKind::Infer => hir::TyKind::Infer,
+            TyKind::Infer => hir::TyKind::Infer(()),
             TyKind::Err(guar) => hir::TyKind::Err(*guar),
             TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
             TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
@@ -1238,7 +1246,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     safety: self.lower_safety(f.safety, hir::Safety::Safe),
                     abi: self.lower_extern(f.ext),
                     decl: self.lower_fn_decl(&f.decl, t.id, t.span, FnDeclKind::Pointer, None),
-                    param_names: self.lower_fn_params_to_names(&f.decl),
+                    param_idents: self.lower_fn_params_to_idents(&f.decl),
                 }))
             }
             TyKind::UnsafeBinder(f) => {
@@ -1309,7 +1317,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span));
                     (bounds, lifetime_bound)
                 });
-                hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
+                hir::TyKind::TraitObject(bounds, TaggedRef::new(lifetime_bound, *kind))
             }
             TyKind::ImplTrait(def_node_id, bounds) => {
                 let span = t.span;
@@ -1366,7 +1374,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     }
                 }
             }
-            TyKind::Pat(ty, pat) => hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_pat(pat)),
+            TyKind::Pat(ty, pat) => {
+                hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span))
+            }
             TyKind::MacCall(_) => {
                 span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
             }
@@ -1430,28 +1440,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // frequently opened issues show.
         let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
 
-        // Feature gate for RPITIT + use<..>
-        match origin {
-            rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
-                if !self.tcx.features().precise_capturing_in_traits()
-                    && let Some(span) = bounds.iter().find_map(|bound| match *bound {
-                        ast::GenericBound::Use(_, span) => Some(span),
-                        _ => None,
-                    })
-                {
-                    let mut diag =
-                        self.tcx.dcx().create_err(errors::NoPreciseCapturesOnRpitit { span });
-                    add_feature_diagnostics(
-                        &mut diag,
-                        self.tcx.sess,
-                        sym::precise_capturing_in_traits,
-                    );
-                    diag.emit();
-                }
-            }
-            _ => {}
-        }
-
         self.lower_opaque_inner(opaque_ty_node_id, origin, opaque_ty_span, |this| {
             this.lower_param_bounds(bounds, itctx)
         })
@@ -1505,10 +1493,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }))
     }
 
-    fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
+    fn lower_fn_params_to_idents(&mut self, decl: &FnDecl) -> &'hir [Option<Ident>] {
         self.arena.alloc_from_iter(decl.inputs.iter().map(|param| match param.pat.kind {
-            PatKind::Ident(_, ident, _) => self.lower_ident(ident),
-            _ => Ident::new(kw::Empty, self.lower_span(param.pat.span)),
+            PatKind::Missing => None,
+            PatKind::Ident(_, ident, _) => Some(self.lower_ident(ident)),
+            PatKind::Wild => Some(Ident::new(kw::Underscore, self.lower_span(param.pat.span))),
+            _ => {
+                self.dcx().span_delayed_bug(
+                    param.pat.span,
+                    "non-missing/ident/wild param pat must trigger an error",
+                );
+                None
+            }
         }))
     }
 
@@ -1653,7 +1649,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None),
             CoroutineKind::Gen { return_impl_trait_id, .. } => (return_impl_trait_id, None),
             CoroutineKind::AsyncGen { return_impl_trait_id, .. } => {
-                (return_impl_trait_id, Some(Lrc::clone(&self.allow_async_iterator)))
+                (return_impl_trait_id, Some(Arc::clone(&self.allow_async_iterator)))
             }
         };
 
@@ -1754,56 +1750,62 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     }
 
     fn lower_lifetime(&mut self, l: &Lifetime) -> &'hir hir::Lifetime {
-        let ident = self.lower_ident(l.ident);
-        self.new_named_lifetime(l.id, l.id, ident)
+        self.new_named_lifetime(l.id, l.id, l.ident, IsAnonInPath::No)
+    }
+
+    fn lower_lifetime_anon_in_path(&mut self, id: NodeId, span: Span) -> &'hir hir::Lifetime {
+        self.new_named_lifetime(id, id, Ident::new(kw::UnderscoreLifetime, span), IsAnonInPath::Yes)
     }
 
     #[instrument(level = "debug", skip(self))]
-    fn new_named_lifetime_with_res(
+    fn new_named_lifetime(
         &mut self,
         id: NodeId,
+        new_id: NodeId,
         ident: Ident,
-        res: LifetimeRes,
+        is_anon_in_path: IsAnonInPath,
     ) -> &'hir hir::Lifetime {
+        let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error);
         let res = match res {
-            LifetimeRes::Param { param, .. } => hir::LifetimeName::Param(param),
+            LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param),
             LifetimeRes::Fresh { param, .. } => {
+                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
                 let param = self.local_def_id(param);
-                hir::LifetimeName::Param(param)
+                hir::LifetimeKind::Param(param)
+            }
+            LifetimeRes::Infer => {
+                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                hir::LifetimeKind::Infer
+            }
+            LifetimeRes::Static { .. } => {
+                debug_assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
+                hir::LifetimeKind::Static
+            }
+            LifetimeRes::Error => hir::LifetimeKind::Error,
+            LifetimeRes::ElidedAnchor { .. } => {
+                panic!("Unexpected `ElidedAnchar` {:?} at {:?}", ident, ident.span);
             }
-            LifetimeRes::Infer => hir::LifetimeName::Infer,
-            LifetimeRes::Static { .. } => hir::LifetimeName::Static,
-            LifetimeRes::Error => hir::LifetimeName::Error,
-            res => panic!(
-                "Unexpected lifetime resolution {:?} for {:?} at {:?}",
-                res, ident, ident.span
-            ),
         };
 
+        #[cfg(debug_assertions)]
+        if is_anon_in_path == IsAnonInPath::Yes {
+            debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+        }
+
         debug!(?res);
-        self.arena.alloc(hir::Lifetime {
-            hir_id: self.lower_node_id(id),
-            ident: self.lower_ident(ident),
+        self.arena.alloc(hir::Lifetime::new(
+            self.lower_node_id(new_id),
+            self.lower_ident(ident),
             res,
-        })
+            is_anon_in_path,
+        ))
     }
 
-    #[instrument(level = "debug", skip(self))]
-    fn new_named_lifetime(
+    fn lower_generic_params_mut(
         &mut self,
-        id: NodeId,
-        new_id: NodeId,
-        ident: Ident,
-    ) -> &'hir hir::Lifetime {
-        let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error);
-        self.new_named_lifetime_with_res(new_id, ident, res)
-    }
-
-    fn lower_generic_params_mut<'s>(
-        &'s mut self,
-        params: &'s [GenericParam],
+        params: &[GenericParam],
         source: hir::GenericParamSource,
-    ) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> {
+    ) -> impl Iterator<Item = hir::GenericParam<'hir>> {
         params.iter().map(move |param| self.lower_generic_param(param, source))
     }
 
@@ -1824,7 +1826,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let (name, kind) = self.lower_generic_param_kind(param, source);
 
         let hir_id = self.lower_node_id(param.id);
-        self.lower_attrs(hir_id, &param.attrs);
+        self.lower_attrs(hir_id, &param.attrs, param.span());
         hir::GenericParam {
             hir_id,
             def_id: self.local_def_id(param.id),
@@ -1964,11 +1966,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         self.arena.alloc_from_iter(self.lower_param_bounds_mut(bounds, itctx))
     }
 
-    fn lower_param_bounds_mut<'s>(
-        &'s mut self,
-        bounds: &'s [GenericBound],
+    fn lower_param_bounds_mut(
+        &mut self,
+        bounds: &[GenericBound],
         itctx: ImplTraitContext,
-    ) -> impl Iterator<Item = hir::GenericBound<'hir>> + Captures<'s> + Captures<'a> {
+    ) -> impl Iterator<Item = hir::GenericBound<'hir>> {
         bounds.iter().map(move |bound| self.lower_param_bound(bound, itctx))
     }
 
@@ -2030,7 +2032,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     }
 
     fn lower_array_length_to_const_arg(&mut self, c: &AnonConst) -> &'hir hir::ConstArg<'hir> {
-        match c.value.kind {
+        // We cannot just match on `ExprKind::Underscore` as `(_)` is represented as
+        // `ExprKind::Paren(ExprKind::Underscore)` and should also be lowered to `GenericArg::Infer`
+        match c.value.peel_parens().kind {
             ExprKind::Underscore => {
                 if !self.tcx.features().generic_arg_infer() {
                     feature_err(
@@ -2041,7 +2045,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     )
                     .stash(c.value.span, StashKey::UnderscoreForArrayLengths);
                 }
-                let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
+                let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ());
                 self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
             }
             _ => self.lower_anon_const_to_const_arg(c),
@@ -2061,8 +2065,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     ) -> &'hir hir::ConstArg<'hir> {
         let tcx = self.tcx;
 
-        // FIXME(min_generic_const_args): we only allow one-segment const paths for now
-        let ct_kind = if path.is_potential_trivial_const_arg()
+        let ct_kind = if path
+            .is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
             && (tcx.features().min_generic_const_args()
                 || matches!(res, Res::Def(DefKind::ConstParam, _)))
         {
@@ -2072,7 +2076,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 path,
                 ParamMode::Optional,
                 AllowReturnTypeNotation::No,
-                // FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
+                // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
                 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                 None,
             );
@@ -2088,8 +2092,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // We're lowering a const argument that was originally thought to be a type argument,
             // so the def collector didn't create the def ahead of time. That's why we have to do
             // it here.
-            let def_id =
-                self.create_def(parent_def_id, node_id, kw::Empty, DefKind::AnonConst, span);
+            let def_id = self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, span);
             let hir_id = self.lower_node_id(node_id);
 
             let path_expr = Expr {
@@ -2136,19 +2139,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         };
         let maybe_res =
             self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
-        // FIXME(min_generic_const_args): we only allow one-segment const paths for now
-        if let ExprKind::Path(None, path) = &expr.kind
-            && path.is_potential_trivial_const_arg()
+        if let ExprKind::Path(qself, path) = &expr.kind
+            && path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
             && (tcx.features().min_generic_const_args()
                 || matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
         {
             let qpath = self.lower_qpath(
                 expr.id,
-                &None,
+                qself,
                 path,
                 ParamMode::Optional,
                 AllowReturnTypeNotation::No,
-                // FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
+                // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
                 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                 None,
             );
@@ -2216,6 +2218,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             self.attrs.insert(hir_id.local_id, a);
         }
         let local = hir::LetStmt {
+            super_: None,
             hir_id,
             init,
             pat,
@@ -2365,8 +2368,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         hir_id = self.next_id();
                         hir::TyKind::TraitObject(
                             arena_vec![self; principal],
-                            self.elided_dyn_bound(span),
-                            TraitObjectSyntax::None,
+                            TaggedRef::new(self.elided_dyn_bound(span), TraitObjectSyntax::None),
                         )
                     }
                     _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)),
@@ -2383,11 +2385,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     /// when the bound is written, even if it is written with `'_` like in
     /// `Box<dyn Debug + '_>`. In those cases, `lower_lifetime` is invoked.
     fn elided_dyn_bound(&mut self, span: Span) -> &'hir hir::Lifetime {
-        let r = hir::Lifetime {
-            hir_id: self.next_id(),
-            ident: Ident::new(kw::Empty, self.lower_span(span)),
-            res: hir::LifetimeName::ImplicitObjectLifetimeDefault,
-        };
+        let r = hir::Lifetime::new(
+            self.next_id(),
+            Ident::new(kw::UnderscoreLifetime, self.lower_span(span)),
+            hir::LifetimeKind::ImplicitObjectLifetimeDefault,
+            IsAnonInPath::No,
+        );
         debug!("elided_dyn_bound: r={:?}", r);
         self.arena.alloc(r)
     }
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index abd314ae74c3..f94d788a9b0e 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -3,11 +3,11 @@ use std::sync::Arc;
 use rustc_ast::ptr::P;
 use rustc_ast::*;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_hir as hir;
-use rustc_hir::def::Res;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{self as hir, LangItem};
 use rustc_middle::span_bug;
 use rustc_span::source_map::{Spanned, respan};
-use rustc_span::{Ident, Span};
+use rustc_span::{DesugaringKind, Ident, Span};
 
 use super::errors::{
     ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
@@ -26,6 +26,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             let pat_hir_id = self.lower_node_id(pattern.id);
             let node = loop {
                 match &pattern.kind {
+                    PatKind::Missing => break hir::PatKind::Missing,
                     PatKind::Wild => break hir::PatKind::Wild,
                     PatKind::Never => break hir::PatKind::Never,
                     PatKind::Ident(binding_mode, ident, sub) => {
@@ -69,7 +70,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                             None,
                         );
-                        break hir::PatKind::Path(qpath);
+                        let kind = hir::PatExprKind::Path(qpath);
+                        let span = self.lower_span(pattern.span);
+                        let expr = hir::PatExpr { hir_id: pat_hir_id, span, kind };
+                        let expr = self.arena.alloc(expr);
+                        return hir::Pat {
+                            hir_id: self.next_id(),
+                            kind: hir::PatKind::Expr(expr),
+                            span,
+                            default_binding_modes: true,
+                        };
                     }
                     PatKind::Struct(qself, path, fields, etc) => {
                         let qpath = self.lower_qpath(
@@ -84,7 +94,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                         let fs = self.arena.alloc_from_iter(fields.iter().map(|f| {
                             let hir_id = self.lower_node_id(f.id);
-                            self.lower_attrs(hir_id, &f.attrs);
+                            self.lower_attrs(hir_id, &f.attrs, f.span);
 
                             hir::PatField {
                                 hir_id,
@@ -304,16 +314,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 )
             }
             Some(res) => {
-                let hir_id = self.next_id();
                 let res = self.lower_res(res);
-                hir::PatKind::Path(hir::QPath::Resolved(
-                    None,
-                    self.arena.alloc(hir::Path {
-                        span: self.lower_span(ident.span),
-                        res,
-                        segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],
-                    }),
-            ))
+                let span = self.lower_span(ident.span);
+                hir::PatKind::Expr(self.arena.alloc(hir::PatExpr {
+                    kind: hir::PatExprKind::Path(hir::QPath::Resolved(
+                        None,
+                        self.arena.alloc(hir::Path {
+                            span,
+                            res,
+                            segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), self.next_id(), res)],
+                        }),
+                    )),
+                    hir_id: self.next_id(),
+                    span,
+                }))
             }
         }
     }
@@ -375,24 +389,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         expr: &Expr,
         allow_paths: bool,
     ) -> &'hir hir::PatExpr<'hir> {
+        let span = self.lower_span(expr.span);
         let err = |guar| hir::PatExprKind::Lit {
-            lit: self.arena.alloc(respan(self.lower_span(expr.span), LitKind::Err(guar))),
+            lit: self.arena.alloc(respan(span, LitKind::Err(guar))),
             negated: false,
         };
         let kind = match &expr.kind {
             ExprKind::Lit(lit) => {
-                hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: false }
+                hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false }
             }
             ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)),
             ExprKind::IncludedBytes(bytes) => hir::PatExprKind::Lit {
-                lit: self.arena.alloc(respan(
-                    self.lower_span(expr.span),
-                    LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked),
-                )),
+                lit: self
+                    .arena
+                    .alloc(respan(span, LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked))),
                 negated: false,
             },
             ExprKind::Err(guar) => err(*guar),
-            ExprKind::Dummy => span_bug!(expr.span, "lowered ExprKind::Dummy"),
+            ExprKind::Dummy => span_bug!(span, "lowered ExprKind::Dummy"),
             ExprKind::Path(qself, path) if allow_paths => hir::PatExprKind::Path(self.lower_qpath(
                 expr.id,
                 qself,
@@ -403,21 +417,138 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 None,
             )),
             ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => {
-                hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: true }
+                hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true }
             }
             _ => {
                 let pattern_from_macro = expr.is_approximately_pattern();
                 let guar = self.dcx().emit_err(ArbitraryExpressionInPattern {
-                    span: expr.span,
+                    span,
                     pattern_from_macro_note: pattern_from_macro,
                 });
                 err(guar)
             }
         };
-        self.arena.alloc(hir::PatExpr {
-            hir_id: self.lower_node_id(expr.id),
-            span: expr.span,
-            kind,
+        self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
+    }
+
+    pub(crate) fn lower_ty_pat(
+        &mut self,
+        pattern: &TyPat,
+        base_type: Span,
+    ) -> &'hir hir::TyPat<'hir> {
+        self.arena.alloc(self.lower_ty_pat_mut(pattern, base_type))
+    }
+
+    fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> {
+        // loop here to avoid recursion
+        let pat_hir_id = self.lower_node_id(pattern.id);
+        let node = match &pattern.kind {
+            TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range(
+                e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| {
+                    self.lower_ty_pat_range_end(
+                        hir::LangItem::RangeMin,
+                        span.shrink_to_lo(),
+                        base_type,
+                    )
+                }),
+                e2.as_deref()
+                    .map(|e| match end {
+                        RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e),
+                        RangeEnd::Excluded => self.lower_excluded_range_end(e),
+                    })
+                    .unwrap_or_else(|| {
+                        self.lower_ty_pat_range_end(
+                            hir::LangItem::RangeMax,
+                            span.shrink_to_hi(),
+                            base_type,
+                        )
+                    }),
+            ),
+            TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar),
+        };
+
+        hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
+    }
+
+    /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1).
+    /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges.
+    fn lower_excluded_range_end(&mut self, e: &AnonConst) -> &'hir hir::ConstArg<'hir> {
+        let span = self.lower_span(e.value.span);
+        let unstable_span = self.mark_span_with_reason(
+            DesugaringKind::PatTyRange,
+            span,
+            Some(Arc::clone(&self.allow_pattern_type)),
+        );
+        let anon_const = self.with_new_scopes(span, |this| {
+            let def_id = this.local_def_id(e.id);
+            let hir_id = this.lower_node_id(e.id);
+            let body = this.lower_body(|this| {
+                // Need to use a custom function as we can't just subtract `1` from a `char`.
+                let kind = hir::ExprKind::Path(this.make_lang_item_qpath(
+                    hir::LangItem::RangeSub,
+                    unstable_span,
+                    None,
+                ));
+                let fn_def = this.arena.alloc(hir::Expr { hir_id: this.next_id(), kind, span });
+                let args = this.arena.alloc([this.lower_expr_mut(&e.value)]);
+                (
+                    &[],
+                    hir::Expr {
+                        hir_id: this.next_id(),
+                        kind: hir::ExprKind::Call(fn_def, args),
+                        span,
+                    },
+                )
+            });
+            hir::AnonConst { def_id, hir_id, body, span }
+        });
+        self.arena.alloc(hir::ConstArg {
+            hir_id: self.next_id(),
+            kind: hir::ConstArgKind::Anon(self.arena.alloc(anon_const)),
         })
     }
+
+    /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`),
+    /// we instead use a constant of the MAX/MIN of the type.
+    /// This way the type system does not have to handle the lack of a start/end.
+    fn lower_ty_pat_range_end(
+        &mut self,
+        lang_item: LangItem,
+        span: Span,
+        base_type: Span,
+    ) -> &'hir hir::ConstArg<'hir> {
+        let parent_def_id = self.current_hir_id_owner.def_id;
+        let node_id = self.next_node_id();
+
+        // Add a definition for the in-band const def.
+        // We're generating a range end that didn't exist in the AST,
+        // so the def collector didn't create the def ahead of time. That's why we have to do
+        // it here.
+        let def_id = self.create_def(parent_def_id, node_id, None, DefKind::AnonConst, span);
+        let hir_id = self.lower_node_id(node_id);
+
+        let unstable_span = self.mark_span_with_reason(
+            DesugaringKind::PatTyRange,
+            self.lower_span(span),
+            Some(Arc::clone(&self.allow_pattern_type)),
+        );
+        let span = self.lower_span(base_type);
+
+        let path_expr = hir::Expr {
+            hir_id: self.next_id(),
+            kind: hir::ExprKind::Path(self.make_lang_item_qpath(lang_item, unstable_span, None)),
+            span,
+        };
+
+        let ct = self.with_new_scopes(span, |this| {
+            self.arena.alloc(hir::AnonConst {
+                def_id,
+                hir_id,
+                body: this.lower_body(|_this| (&[], path_expr)),
+                span,
+            })
+        });
+        let hir_id = self.next_id();
+        self.arena.alloc(hir::ConstArg { kind: hir::ConstArgKind::Anon(ct), hir_id })
+    }
 }
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 043144a54649..0bd65aec10f4 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -1,18 +1,19 @@
+use std::sync::Arc;
+
 use rustc_ast::{self as ast, *};
-use rustc_data_structures::sync::Lrc;
 use rustc_hir as hir;
 use rustc_hir::GenericArg;
 use rustc_hir::def::{DefKind, PartialRes, Res};
 use rustc_hir::def_id::DefId;
 use rustc_middle::span_bug;
 use rustc_session::parse::add_feature_diagnostics;
-use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
+use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
 use smallvec::{SmallVec, smallvec};
 use tracing::{debug, instrument};
 
 use super::errors::{
     AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation,
-    GenericTypeWithParentheses, UseAngleBrackets,
+    GenericTypeWithParentheses, RTNSuggestion, UseAngleBrackets,
 };
 use super::{
     AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition,
@@ -72,7 +73,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let bound_modifier_allowed_features = if let Res::Def(DefKind::Trait, async_def_id) = res
             && self.tcx.async_fn_trait_kind_from_def_id(async_def_id).is_some()
         {
-            Some(Lrc::clone(&self.allow_async_fn_traits))
+            Some(Arc::clone(&self.allow_async_fn_traits))
         } else {
             None
         };
@@ -257,7 +258,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // Additional features ungated with a bound modifier like `async`.
         // This is passed down to the implicit associated type binding in
         // parenthesized bounds.
-        bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
+        bound_modifier_allowed_features: Option<Arc<[Symbol]>>,
     ) -> hir::PathSegment<'hir> {
         debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment);
         let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() {
@@ -267,19 +268,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 }
                 GenericArgs::Parenthesized(data) => match generic_args_mode {
                     GenericArgsMode::ReturnTypeNotation => {
-                        let mut err = if !data.inputs.is_empty() {
-                            self.dcx().create_err(BadReturnTypeNotation::Inputs {
-                                span: data.inputs_span,
-                            })
-                        } else if let FnRetTy::Ty(ty) = &data.output {
-                            self.dcx().create_err(BadReturnTypeNotation::Output {
-                                span: data.inputs_span.shrink_to_hi().to(ty.span),
-                            })
-                        } else {
-                            self.dcx().create_err(BadReturnTypeNotation::NeedsDots {
-                                span: data.inputs_span,
-                            })
+                        let err = match (&data.inputs[..], &data.output) {
+                            ([_, ..], FnRetTy::Default(_)) => {
+                                BadReturnTypeNotation::Inputs { span: data.inputs_span }
+                            }
+                            ([], FnRetTy::Default(_)) => {
+                                BadReturnTypeNotation::NeedsDots { span: data.inputs_span }
+                            }
+                            // The case `T: Trait<method(..) -> Ret>` is handled in the parser.
+                            (_, FnRetTy::Ty(ty)) => {
+                                let span = data.inputs_span.shrink_to_hi().to(ty.span);
+                                BadReturnTypeNotation::Output {
+                                    span,
+                                    suggestion: RTNSuggestion {
+                                        output: span,
+                                        input: data.inputs_span,
+                                    },
+                                }
+                            }
                         };
+                        let mut err = self.dcx().create_err(err);
                         if !self.tcx.features().return_type_notation()
                             && self.tcx.sess.is_nightly_build()
                         {
@@ -440,12 +448,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         generic_args.args.insert_many(
             0,
-            (start.as_u32()..end.as_u32()).map(|i| {
-                let id = NodeId::from_u32(i);
-                let l = self.lower_lifetime(&Lifetime {
-                    id,
-                    ident: Ident::new(kw::Empty, elided_lifetime_span),
-                });
+            (start..end).map(|id| {
+                let l = self.lower_lifetime_anon_in_path(id, elided_lifetime_span);
                 GenericArg::Lifetime(l)
             }),
         );
@@ -490,7 +494,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         data: &ParenthesizedArgs,
         itctx: ImplTraitContext,
-        bound_modifier_allowed_features: Option<Lrc<[Symbol]>>,
+        bound_modifier_allowed_features: Option<Arc<[Symbol]>>,
     ) -> (GenericArgsCtor<'hir>, bool) {
         // Switch to `PassThrough` mode for anonymous lifetimes; this
         // means that we permit things like `&Ref<T>`, where `Ref` has
@@ -525,7 +529,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
             FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
         };
-        let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
+        let args = smallvec![GenericArg::Type(
+            self.arena.alloc(self.ty_tup(*inputs_span, inputs)).try_as_ambig_ty().unwrap()
+        )];
 
         // If we have a bound like `async Fn() -> T`, make sure that we mark the
         // `Output = T` associated type bound with the right feature gates.
diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs
new file mode 100644
index 000000000000..eb052ba1c6d7
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/stability.rs
@@ -0,0 +1,138 @@
+use std::fmt;
+
+use rustc_abi::ExternAbi;
+use rustc_feature::Features;
+use rustc_session::Session;
+use rustc_session::parse::feature_err;
+use rustc_span::symbol::sym;
+use rustc_span::{Span, Symbol};
+
+pub(crate) fn enabled_names(features: &rustc_feature::Features, span: Span) -> Vec<&'static str> {
+    ExternAbi::ALL_VARIANTS
+        .into_iter()
+        .filter(|abi| extern_abi_enabled(features, span, **abi).is_ok())
+        .map(|abi| abi.as_str())
+        .collect()
+}
+
+pub(crate) fn extern_abi_enabled(
+    features: &rustc_feature::Features,
+    span: Span,
+    abi: ExternAbi,
+) -> Result<(), UnstableAbi> {
+    extern_abi_stability(abi).or_else(|unstable @ UnstableAbi { feature, .. }| {
+        if features.enabled(feature) || span.allows_unstable(feature) {
+            Ok(())
+        } else {
+            Err(unstable)
+        }
+    })
+}
+
+#[allow(rustc::untranslatable_diagnostic)]
+pub(crate) fn gate_unstable_abi(sess: &Session, features: &Features, span: Span, abi: ExternAbi) {
+    match extern_abi_enabled(features, span, abi) {
+        Ok(_) => (),
+        Err(unstable_abi) => {
+            let explain = unstable_abi.to_string();
+            feature_err(sess, unstable_abi.feature, span, explain).emit();
+        }
+    }
+}
+
+pub struct UnstableAbi {
+    abi: ExternAbi,
+    feature: Symbol,
+    explain: GateReason,
+}
+
+enum GateReason {
+    Experimental,
+    ImplDetail,
+}
+
+impl fmt::Display for UnstableAbi {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let Self { abi, .. } = self;
+        match self.explain {
+            GateReason::Experimental => {
+                write!(f, "the extern {abi} ABI is experimental and subject to change")
+            }
+            GateReason::ImplDetail => {
+                write!(f, "the extern {abi} ABI is an implementation detail and perma-unstable")
+            }
+        }
+    }
+}
+
+pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
+    match abi {
+        // stable ABIs
+        ExternAbi::Rust
+        | ExternAbi::C { .. }
+        | ExternAbi::Cdecl { .. }
+        | ExternAbi::Stdcall { .. }
+        | ExternAbi::Fastcall { .. }
+        | ExternAbi::Thiscall { .. }
+        | ExternAbi::Aapcs { .. }
+        | ExternAbi::Win64 { .. }
+        | ExternAbi::SysV64 { .. }
+        | ExternAbi::System { .. }
+        | ExternAbi::EfiApi => Ok(()),
+        ExternAbi::Unadjusted => {
+            Err(UnstableAbi { abi, feature: sym::abi_unadjusted, explain: GateReason::ImplDetail })
+        }
+        // experimental
+        ExternAbi::Vectorcall { .. } => Err(UnstableAbi {
+            abi,
+            feature: sym::abi_vectorcall,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::RustCall => Err(UnstableAbi {
+            abi,
+            feature: sym::unboxed_closures,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::RustCold => {
+            Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental })
+        }
+        ExternAbi::GpuKernel => Err(UnstableAbi {
+            abi,
+            feature: sym::abi_gpu_kernel,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::PtxKernel => {
+            Err(UnstableAbi { abi, feature: sym::abi_ptx, explain: GateReason::Experimental })
+        }
+        ExternAbi::Msp430Interrupt => Err(UnstableAbi {
+            abi,
+            feature: sym::abi_msp430_interrupt,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::X86Interrupt => Err(UnstableAbi {
+            abi,
+            feature: sym::abi_x86_interrupt,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::AvrInterrupt | ExternAbi::AvrNonBlockingInterrupt => Err(UnstableAbi {
+            abi,
+            feature: sym::abi_avr_interrupt,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::RiscvInterruptM | ExternAbi::RiscvInterruptS => Err(UnstableAbi {
+            abi,
+            feature: sym::abi_riscv_interrupt,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::CCmseNonSecureCall => Err(UnstableAbi {
+            abi,
+            feature: sym::abi_c_cmse_nonsecure_call,
+            explain: GateReason::Experimental,
+        }),
+        ExternAbi::CCmseNonSecureEntry => Err(UnstableAbi {
+            abi,
+            feature: sym::cmse_nonsecure_entry,
+            explain: GateReason::Experimental,
+        }),
+    }
+}
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index 8046765647e8..c738cb2aa2fd 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "rustc_ast_passes"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
 itertools = "0.12"
+rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_attr_parsing = { path = "../rustc_attr_parsing" }
@@ -17,6 +18,5 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_parse = { path = "../rustc_parse" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
-rustc_target = { path = "../rustc_target" }
 thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 5a0ec865f9d4..80754a8f65a6 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -79,6 +79,10 @@ ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$de
     .suggestion = remove the {$remove_descr}
     .label = `extern` block begins here
 
+ast_passes_extern_without_abi = `extern` declarations without an explicit ABI are disallowed
+    .suggestion = specify an ABI
+    .help = prior to Rust 2024, a default ABI was inferred
+
 ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
     .suggestion = remove the attribute
     .stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable
@@ -207,8 +211,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing
 
 ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
 
-ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
-
 ast_passes_static_without_body =
     free static item without body
     .suggestion = provide a definition for the static
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 236ca7ba7035..9a7b7daabbf1 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -20,6 +20,7 @@ use std::mem;
 use std::ops::{Deref, DerefMut};
 
 use itertools::{Either, Itertools};
+use rustc_abi::ExternAbi;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
 use rustc_ast::*;
@@ -35,7 +36,6 @@ use rustc_session::lint::builtin::{
 };
 use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
 use rustc_span::{Ident, Span, kw, sym};
-use rustc_target::spec::abi;
 use thin_vec::thin_vec;
 
 use crate::errors::{self, TildeConstReason};
@@ -47,14 +47,14 @@ enum SelfSemantic {
 }
 
 enum TraitOrTraitImpl {
-    Trait { span: Span, constness: Option<Span> },
-    TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref: Span },
+    Trait { span: Span, constness_span: Option<Span> },
+    TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span },
 }
 
 impl TraitOrTraitImpl {
     fn constness(&self) -> Option<Span> {
         match self {
-            Self::Trait { constness: Some(span), .. }
+            Self::Trait { constness_span: Some(span), .. }
             | Self::TraitImpl { constness: Const::Yes(span), .. } => Some(*span),
             _ => None,
         }
@@ -66,7 +66,7 @@ struct AstValidator<'a> {
     features: &'a Features,
 
     /// The span of the `extern` in an `extern { ... }` block, if any.
-    extern_mod: Option<Span>,
+    extern_mod_span: Option<Span>,
 
     outer_trait_or_trait_impl: Option<TraitOrTraitImpl>,
 
@@ -75,7 +75,7 @@ struct AstValidator<'a> {
     /// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
     /// Nested `impl Trait` _is_ allowed in associated type position,
     /// e.g., `impl Iterator<Item = impl Debug>`.
-    outer_impl_trait: Option<Span>,
+    outer_impl_trait_span: Option<Span>,
 
     disallow_tilde_const: Option<TildeConstReason>,
 
@@ -96,17 +96,22 @@ impl<'a> AstValidator<'a> {
             trait_.map(|(constness, polarity, trait_ref)| TraitOrTraitImpl::TraitImpl {
                 constness,
                 polarity,
-                trait_ref: trait_ref.path.span,
+                trait_ref_span: trait_ref.path.span,
             }),
         );
         f(self);
         self.outer_trait_or_trait_impl = old;
     }
 
-    fn with_in_trait(&mut self, span: Span, constness: Option<Span>, f: impl FnOnce(&mut Self)) {
+    fn with_in_trait(
+        &mut self,
+        span: Span,
+        constness_span: Option<Span>,
+        f: impl FnOnce(&mut Self),
+    ) {
         let old = mem::replace(
             &mut self.outer_trait_or_trait_impl,
-            Some(TraitOrTraitImpl::Trait { span, constness }),
+            Some(TraitOrTraitImpl::Trait { span, constness_span }),
         );
         f(self);
         self.outer_trait_or_trait_impl = old;
@@ -170,10 +175,10 @@ impl<'a> AstValidator<'a> {
         Err(errors::WhereClauseBeforeTypeAlias { span, sugg })
     }
 
-    fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
-        let old = mem::replace(&mut self.outer_impl_trait, outer);
+    fn with_impl_trait(&mut self, outer_span: Option<Span>, f: impl FnOnce(&mut Self)) {
+        let old = mem::replace(&mut self.outer_impl_trait_span, outer_span);
         f(self);
-        self.outer_impl_trait = old;
+        self.outer_impl_trait_span = old;
     }
 
     // Mirrors `visit::walk_ty`, but tracks relevant state.
@@ -239,7 +244,7 @@ impl<'a> AstValidator<'a> {
     fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) {
         for Param { pat, .. } in &decl.inputs {
             match pat.kind {
-                PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {}
+                PatKind::Missing | PatKind::Ident(BindingMode::NONE, _, None) | PatKind::Wild => {}
                 PatKind::Ident(BindingMode::MUT, ident, None) => {
                     report_err(pat.span, Some(ident), true)
                 }
@@ -258,21 +263,22 @@ impl<'a> AstValidator<'a> {
             && let TraitOrTraitImpl::TraitImpl {
                 constness: Const::No,
                 polarity: ImplPolarity::Positive,
-                trait_ref,
+                trait_ref_span,
                 ..
             } = parent
         {
-            Some(trait_ref.shrink_to_lo())
+            Some(trait_ref_span.shrink_to_lo())
         } else {
             None
         };
 
-        let make_trait_const_sugg =
-            if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent {
-                Some(span.shrink_to_lo())
-            } else {
-                None
-            };
+        let make_trait_const_sugg = if const_trait_impl
+            && let TraitOrTraitImpl::Trait { span, constness_span: None } = parent
+        {
+            Some(span.shrink_to_lo())
+        } else {
+            None
+        };
 
         let parent_constness = parent.constness();
         self.dcx().emit_err(errors::TraitFnConst {
@@ -334,8 +340,8 @@ impl<'a> AstValidator<'a> {
             .filter(|attr| {
                 let arr = [
                     sym::allow,
-                    sym::cfg,
-                    sym::cfg_attr,
+                    sym::cfg_trace,
+                    sym::cfg_attr_trace,
                     sym::deny,
                     sym::expect,
                     sym::forbid,
@@ -448,13 +454,13 @@ impl<'a> AstValidator<'a> {
         check_where_clause(where_clauses.after);
     }
 
-    fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option<Span>) {
-        let Some(body) = body else {
+    fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body_span: Option<Span>) {
+        let Some(body_span) = body_span else {
             return;
         };
         self.dcx().emit_err(errors::BodyInExtern {
             span: ident.span,
-            body,
+            body: body_span,
             block: self.current_extern_span(),
             kind,
         });
@@ -473,7 +479,7 @@ impl<'a> AstValidator<'a> {
     }
 
     fn current_extern_span(&self) -> Span {
-        self.sess.source_map().guess_head_span(self.extern_mod.unwrap())
+        self.sess.source_map().guess_head_span(self.extern_mod_span.unwrap())
     }
 
     /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
@@ -583,9 +589,10 @@ impl<'a> AstValidator<'a> {
         self.dcx().emit_err(errors::ModuleNonAscii { span: ident.span, name: ident.name });
     }
 
-    fn deny_generic_params(&self, generics: &Generics, ident: Span) {
+    fn deny_generic_params(&self, generics: &Generics, ident_span: Span) {
         if !generics.params.is_empty() {
-            self.dcx().emit_err(errors::AutoTraitGeneric { span: generics.span, ident });
+            self.dcx()
+                .emit_err(errors::AutoTraitGeneric { span: generics.span, ident: ident_span });
         }
     }
 
@@ -605,11 +612,11 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn deny_items(&self, trait_items: &[P<AssocItem>], ident: Span) {
+    fn deny_items(&self, trait_items: &[P<AssocItem>], ident_span: Span) {
         if !trait_items.is_empty() {
-            let spans: Vec<_> = trait_items.iter().map(|i| i.ident.span).collect();
+            let spans: Vec<_> = trait_items.iter().map(|i| i.kind.ident().unwrap().span).collect();
             let total = trait_items.first().unwrap().span.to(trait_items.last().unwrap().span);
-            self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident });
+            self.dcx().emit_err(errors::AutoTraitItems { spans, total, ident: ident_span });
         }
     }
 
@@ -677,7 +684,7 @@ impl<'a> AstValidator<'a> {
                     self.dcx().emit_err(errors::PatternFnPointer { span });
                 });
                 if let Extern::Implicit(extern_span) = bfty.ext {
-                    self.maybe_lint_missing_abi(extern_span, ty.id);
+                    self.handle_missing_abi(extern_span, ty.id);
                 }
             }
             TyKind::TraitObject(bounds, ..) => {
@@ -694,7 +701,7 @@ impl<'a> AstValidator<'a> {
                 }
             }
             TyKind::ImplTrait(_, bounds) => {
-                if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
+                if let Some(outer_impl_trait_sp) = self.outer_impl_trait_span {
                     self.dcx().emit_err(errors::NestedImplTrait {
                         span: ty.span,
                         outer: outer_impl_trait_sp,
@@ -710,10 +717,12 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId) {
+    fn handle_missing_abi(&mut self, span: Span, id: NodeId) {
         // FIXME(davidtwco): This is a hack to detect macros which produce spans of the
         // call site which do not have a macro backtrace. See #61963.
-        if self
+        if span.edition().at_least_edition_future() && self.features.explicit_extern_abis() {
+            self.dcx().emit_err(errors::MissingAbi { span });
+        } else if self
             .sess
             .source_map()
             .span_to_snippet(span)
@@ -723,10 +732,23 @@ impl<'a> AstValidator<'a> {
                 MISSING_ABI,
                 id,
                 span,
-                BuiltinLintDiag::MissingAbi(span, abi::Abi::FALLBACK),
+                BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK),
             )
         }
     }
+
+    // Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
+    fn visit_attrs_vis(&mut self, attrs: &'a AttrVec, vis: &'a Visibility) {
+        walk_list!(self, visit_attribute, attrs);
+        self.visit_vis(vis);
+    }
+
+    // Used within `visit_item` for item kinds where we don't call `visit::walk_item`.
+    fn visit_attrs_vis_ident(&mut self, attrs: &'a AttrVec, vis: &'a Visibility, ident: &'a Ident) {
+        walk_list!(self, visit_attribute, attrs);
+        self.visit_vis(vis);
+        self.visit_ident(ident);
+    }
 }
 
 /// Checks that generic parameters are in the correct order,
@@ -817,8 +839,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.has_proc_macro_decls = true;
         }
 
-        if attr::contains_name(&item.attrs, sym::no_mangle) {
-            self.check_nomangle_item_asciionly(item.ident, item.span);
+        if let Some(ident) = item.kind.ident()
+            && attr::contains_name(&item.attrs, sym::no_mangle)
+        {
+            self.check_nomangle_item_asciionly(ident, item.span);
         }
 
         match &item.kind {
@@ -832,37 +856,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self_ty,
                 items,
             }) => {
-                self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::TraitImpl,
-                    );
-                    if let TyKind::Dummy = self_ty.kind {
-                        // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
-                        // which isn't allowed. Not a problem for this obscure, obsolete syntax.
-                        this.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
-                    }
-                    if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity)
-                    {
-                        this.dcx().emit_err(errors::UnsafeNegativeImpl {
-                            span: sp.to(t.path.span),
-                            negative: sp,
-                            r#unsafe: span,
-                        });
-                    }
+                self.visit_attrs_vis(&item.attrs, &item.vis);
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::TraitImpl,
+                );
+                if let TyKind::Dummy = self_ty.kind {
+                    // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
+                    // which isn't allowed. Not a problem for this obscure, obsolete syntax.
+                    self.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
+                }
+                if let (&Safety::Unsafe(span), &ImplPolarity::Negative(sp)) = (safety, polarity) {
+                    self.dcx().emit_err(errors::UnsafeNegativeImpl {
+                        span: sp.to(t.path.span),
+                        negative: sp,
+                        r#unsafe: span,
+                    });
+                }
 
-                    this.visit_vis(&item.vis);
-                    this.visit_ident(&item.ident);
-                    let disallowed = matches!(constness, Const::No)
-                        .then(|| TildeConstReason::TraitImpl { span: item.span });
-                    this.with_tilde_const(disallowed, |this| this.visit_generics(generics));
-                    this.visit_trait_ref(t);
-                    this.visit_ty(self_ty);
+                let disallowed = matches!(constness, Const::No)
+                    .then(|| TildeConstReason::TraitImpl { span: item.span });
+                self.with_tilde_const(disallowed, |this| this.visit_generics(generics));
+                self.visit_trait_ref(t);
+                self.visit_ty(self_ty);
 
-                    walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl);
+                self.with_in_trait_impl(Some((*constness, *polarity, t)), |this| {
+                    walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true });
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::Impl(box Impl {
                 safety,
@@ -882,42 +902,49 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     only_trait,
                 };
 
-                self.with_in_trait_impl(None, |this| {
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::IndividualImplItems,
-                    );
-                    if let &Safety::Unsafe(span) = safety {
-                        this.dcx().emit_err(errors::InherentImplCannotUnsafe {
-                            span: self_ty.span,
-                            annotation_span: span,
-                            annotation: "unsafe",
-                            self_ty: self_ty.span,
-                        });
-                    }
-                    if let &ImplPolarity::Negative(span) = polarity {
-                        this.dcx().emit_err(error(span, "negative", false));
-                    }
-                    if let &Defaultness::Default(def_span) = defaultness {
-                        this.dcx().emit_err(error(def_span, "`default`", true));
-                    }
-                    if let &Const::Yes(span) = constness {
-                        this.dcx().emit_err(error(span, "`const`", true));
-                    }
+                self.visit_attrs_vis(&item.attrs, &item.vis);
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::IndividualImplItems,
+                );
+                if let &Safety::Unsafe(span) = safety {
+                    self.dcx().emit_err(errors::InherentImplCannotUnsafe {
+                        span: self_ty.span,
+                        annotation_span: span,
+                        annotation: "unsafe",
+                        self_ty: self_ty.span,
+                    });
+                }
+                if let &ImplPolarity::Negative(span) = polarity {
+                    self.dcx().emit_err(error(span, "negative", false));
+                }
+                if let &Defaultness::Default(def_span) = defaultness {
+                    self.dcx().emit_err(error(def_span, "`default`", true));
+                }
+                if let &Const::Yes(span) = constness {
+                    self.dcx().emit_err(error(span, "`const`", true));
+                }
 
-                    this.visit_vis(&item.vis);
-                    this.visit_ident(&item.ident);
-                    this.with_tilde_const(
-                        Some(TildeConstReason::Impl { span: item.span }),
-                        |this| this.visit_generics(generics),
-                    );
-                    this.visit_ty(self_ty);
-                    walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl);
+                self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| {
+                    this.visit_generics(generics)
+                });
+                self.visit_ty(self_ty);
+                self.with_in_trait_impl(None, |this| {
+                    walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: false });
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
-            ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => {
+            ItemKind::Fn(
+                func @ box Fn {
+                    defaultness,
+                    ident,
+                    generics: _,
+                    sig,
+                    contract: _,
+                    body,
+                    define_opaque: _,
+                },
+            ) => {
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                 self.check_defaultness(item.span, *defaultness);
 
                 let is_intrinsic =
@@ -945,45 +972,40 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     });
                 }
 
-                self.visit_vis(&item.vis);
-                self.visit_ident(&item.ident);
-                let kind = FnKind::Fn(FnCtxt::Free, &item.ident, sig, &item.vis, generics, body);
+                let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func);
                 self.visit_fn(kind, item.span, item.id);
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again.
             }
             ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => {
-                self.with_in_extern_mod(*safety, |this| {
-                    let old_item = mem::replace(&mut this.extern_mod, Some(item.span));
-                    this.visibility_not_permitted(
-                        &item.vis,
-                        errors::VisibilityNotPermittedNote::IndividualForeignItems,
-                    );
-
-                    if &Safety::Default == safety {
-                        if item.span.at_least_rust_2024() {
-                            this.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
-                        } else {
-                            this.lint_buffer.buffer_lint(
-                                MISSING_UNSAFE_ON_EXTERN,
-                                item.id,
-                                item.span,
-                                BuiltinLintDiag::MissingUnsafeOnExtern {
-                                    suggestion: item.span.shrink_to_lo(),
-                                },
-                            );
-                        }
+                let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span));
+                self.visibility_not_permitted(
+                    &item.vis,
+                    errors::VisibilityNotPermittedNote::IndividualForeignItems,
+                );
+
+                if &Safety::Default == safety {
+                    if item.span.at_least_rust_2024() {
+                        self.dcx().emit_err(errors::MissingUnsafeOnExtern { span: item.span });
+                    } else {
+                        self.lint_buffer.buffer_lint(
+                            MISSING_UNSAFE_ON_EXTERN,
+                            item.id,
+                            item.span,
+                            BuiltinLintDiag::MissingUnsafeOnExtern {
+                                suggestion: item.span.shrink_to_lo(),
+                            },
+                        );
                     }
+                }
 
-                    if abi.is_none() {
-                        this.maybe_lint_missing_abi(*extern_span, item.id);
-                    }
+                if abi.is_none() {
+                    self.handle_missing_abi(*extern_span, item.id);
+                }
+                self.with_in_extern_mod(*safety, |this| {
                     visit::walk_item(this, item);
-                    this.extern_mod = old_item;
                 });
-                return; // Avoid visiting again.
+                self.extern_mod_span = old_item;
             }
-            ItemKind::Enum(def, _) => {
+            ItemKind::Enum(_, def, _) => {
                 for variant in &def.variants {
                     self.visibility_not_permitted(
                         &variant.vis,
@@ -996,36 +1018,33 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         );
                     }
                 }
+                visit::walk_item(self, item)
             }
-            ItemKind::Trait(box Trait { is_auto, generics, bounds, items, .. }) => {
+            ItemKind::Trait(box Trait { is_auto, generics, ident, bounds, items, .. }) => {
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                 let is_const_trait =
                     attr::find_by_name(&item.attrs, sym::const_trait).map(|attr| attr.span);
-                self.with_in_trait(item.span, is_const_trait, |this| {
-                    if *is_auto == IsAuto::Yes {
-                        // Auto traits cannot have generics, super traits nor contain items.
-                        this.deny_generic_params(generics, item.ident.span);
-                        this.deny_super_traits(bounds, item.ident.span);
-                        this.deny_where_clause(&generics.where_clause, item.ident.span);
-                        this.deny_items(items, item.ident.span);
-                    }
+                if *is_auto == IsAuto::Yes {
+                    // Auto traits cannot have generics, super traits nor contain items.
+                    self.deny_generic_params(generics, ident.span);
+                    self.deny_super_traits(bounds, ident.span);
+                    self.deny_where_clause(&generics.where_clause, ident.span);
+                    self.deny_items(items, ident.span);
+                }
 
-                    // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
-                    // context for the supertraits.
-                    this.visit_vis(&item.vis);
-                    this.visit_ident(&item.ident);
-                    let disallowed = is_const_trait
-                        .is_none()
-                        .then(|| TildeConstReason::Trait { span: item.span });
-                    this.with_tilde_const(disallowed, |this| {
-                        this.visit_generics(generics);
-                        walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
-                    });
+                // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
+                // context for the supertraits.
+                let disallowed =
+                    is_const_trait.is_none().then(|| TildeConstReason::Trait { span: item.span });
+                self.with_tilde_const(disallowed, |this| {
+                    this.visit_generics(generics);
+                    walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
+                });
+                self.with_in_trait(item.span, is_const_trait, |this| {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Trait);
                 });
-                walk_list!(self, visit_attribute, &item.attrs);
-                return; // Avoid visiting again
             }
-            ItemKind::Mod(safety, mod_kind) => {
+            ItemKind::Mod(safety, ident, mod_kind) => {
                 if let &Safety::Unsafe(span) = safety {
                     self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
                 }
@@ -1033,36 +1052,31 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _))
                     && !attr::contains_name(&item.attrs, sym::path)
                 {
-                    self.check_mod_file_item_asciionly(item.ident);
+                    self.check_mod_file_item_asciionly(*ident);
                 }
+                visit::walk_item(self, item)
             }
-            ItemKind::Struct(vdata, generics) => match vdata {
+            ItemKind::Struct(ident, vdata, generics) => match vdata {
                 VariantData::Struct { fields, .. } => {
-                    self.visit_vis(&item.vis);
-                    self.visit_ident(&item.ident);
+                    self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                     self.visit_generics(generics);
                     // Permit `Anon{Struct,Union}` as field type.
                     walk_list!(self, visit_struct_field_def, fields);
-                    walk_list!(self, visit_attribute, &item.attrs);
-                    return;
                 }
-                _ => {}
+                _ => visit::walk_item(self, item),
             },
-            ItemKind::Union(vdata, generics) => {
+            ItemKind::Union(ident, vdata, generics) => {
                 if vdata.fields().is_empty() {
                     self.dcx().emit_err(errors::FieldlessUnion { span: item.span });
                 }
                 match vdata {
                     VariantData::Struct { fields, .. } => {
-                        self.visit_vis(&item.vis);
-                        self.visit_ident(&item.ident);
+                        self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
                         self.visit_generics(generics);
                         // Permit `Anon{Struct,Union}` as field type.
                         walk_list!(self, visit_struct_field_def, fields);
-                        walk_list!(self, visit_attribute, &item.attrs);
-                        return;
                     }
-                    _ => {}
+                    _ => visit::walk_item(self, item),
                 }
             }
             ItemKind::Const(box ConstItem { defaultness, expr, .. }) => {
@@ -1073,6 +1087,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         replace_span: self.ending_semi_or_hi(item.span),
                     });
                 }
+                visit::walk_item(self, item);
             }
             ItemKind::Static(box StaticItem { expr, safety, .. }) => {
                 self.check_item_safety(item.span, *safety);
@@ -1086,6 +1101,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         replace_span: self.ending_semi_or_hi(item.span),
                     });
                 }
+                visit::walk_item(self, item);
             }
             ItemKind::TyAlias(
                 ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. },
@@ -1109,23 +1125,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         help: self.sess.is_nightly_build(),
                     });
                 }
+                visit::walk_item(self, item);
             }
-            _ => {}
+            _ => visit::walk_item(self, item),
         }
-
-        visit::walk_item(self, item);
     }
 
     fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
         match &fi.kind {
-            ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => {
+            ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => {
                 self.check_defaultness(fi.span, *defaultness);
-                self.check_foreign_fn_bodyless(fi.ident, body.as_deref());
+                self.check_foreign_fn_bodyless(*ident, body.as_deref());
                 self.check_foreign_fn_headerless(sig.header);
-                self.check_foreign_item_ascii_only(fi.ident);
+                self.check_foreign_item_ascii_only(*ident);
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
@@ -1133,15 +1149,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 ..
             }) => {
                 self.check_defaultness(fi.span, *defaultness);
-                self.check_foreign_kind_bodyless(fi.ident, "type", ty.as_ref().map(|b| b.span));
+                self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span));
                 self.check_type_no_bounds(bounds, "`extern` blocks");
                 self.check_foreign_ty_genericless(generics, where_clauses);
-                self.check_foreign_item_ascii_only(fi.ident);
+                self.check_foreign_item_ascii_only(*ident);
             }
-            ForeignItemKind::Static(box StaticItem { expr, safety, .. }) => {
+            ForeignItemKind::Static(box StaticItem { ident, safety, expr, .. }) => {
                 self.check_item_safety(fi.span, *safety);
-                self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span));
-                self.check_foreign_item_ascii_only(fi.ident);
+                self.check_foreign_kind_bodyless(*ident, "static", expr.as_ref().map(|b| b.span));
+                self.check_foreign_item_ascii_only(*ident);
             }
             ForeignItemKind::MacCall(..) => {}
         }
@@ -1350,17 +1366,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         if let FnKind::Fn(
             _,
             _,
-            FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. },
-            _,
-            _,
-            _,
+            Fn {
+                sig: FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. },
+                ..
+            },
         ) = fk
         {
-            self.maybe_lint_missing_abi(*extern_span, id);
+            self.handle_missing_abi(*extern_span, id);
         }
 
         // Functions without bodies cannot have patterns.
-        if let FnKind::Fn(ctxt, _, sig, _, _, None) = fk {
+        if let FnKind::Fn(ctxt, _, Fn { body: None, sig, .. }) = fk {
             Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| {
                 if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) {
                     if let Some(ident) = ident {
@@ -1394,22 +1410,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         .is_some();
 
         let disallowed = (!tilde_const_allowed).then(|| match fk {
-            FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span },
+            FnKind::Fn(_, _, f) => TildeConstReason::Function { ident: f.ident.span },
             FnKind::Closure(..) => TildeConstReason::Closure,
         });
         self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk));
     }
 
     fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) {
-        if attr::contains_name(&item.attrs, sym::no_mangle) {
-            self.check_nomangle_item_asciionly(item.ident, item.span);
+        if let Some(ident) = item.kind.ident()
+            && attr::contains_name(&item.attrs, sym::no_mangle)
+        {
+            self.check_nomangle_item_asciionly(ident, item.span);
         }
 
         if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() {
             self.check_defaultness(item.span, item.kind.defaultness());
         }
 
-        if ctxt == AssocCtxt::Impl {
+        if let AssocCtxt::Impl { .. } = ctxt {
             match &item.kind {
                 AssocItemKind::Const(box ConstItem { expr: None, .. }) => {
                     self.dcx().emit_err(errors::AssocConstWithoutBody {
@@ -1462,24 +1480,21 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
         }
 
-        if let AssocItemKind::Const(..) = item.kind {
-            self.check_item_named(item.ident, "const");
+        if let AssocItemKind::Const(ci) = &item.kind {
+            self.check_item_named(ci.ident, "const");
         }
 
         let parent_is_const =
             self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrTraitImpl::constness).is_some();
 
         match &item.kind {
-            AssocItemKind::Fn(box Fn { sig, generics, body, .. })
+            AssocItemKind::Fn(func)
                 if parent_is_const
                     || ctxt == AssocCtxt::Trait
-                    || matches!(sig.header.constness, Const::Yes(_)) =>
+                    || matches!(func.sig.header.constness, Const::Yes(_)) =>
             {
-                self.visit_vis(&item.vis);
-                self.visit_ident(&item.ident);
-                let kind =
-                    FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, sig, &item.vis, generics, body);
-                walk_list!(self, visit_attribute, &item.attrs);
+                self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
+                let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func);
                 self.visit_fn(kind, item.span, item.id);
             }
             AssocItemKind::Type(_) => {
@@ -1584,7 +1599,7 @@ fn deny_equality_constraints(
                     generics.where_clause.span
                 } else {
                     let mut span = predicate_span;
-                    let mut prev: Option<Span> = None;
+                    let mut prev_span: Option<Span> = None;
                     let mut preds = generics.where_clause.predicates.iter().peekable();
                     // Find the predicate that shouldn't have been in the where bound list.
                     while let Some(pred) = preds.next() {
@@ -1594,12 +1609,12 @@ fn deny_equality_constraints(
                             if let Some(next) = preds.peek() {
                                 // This is the first predicate, remove the trailing comma as well.
                                 span = span.with_hi(next.span.lo());
-                            } else if let Some(prev) = prev {
+                            } else if let Some(prev_span) = prev_span {
                                 // Remove the previous comma as well.
-                                span = span.with_lo(prev.hi());
+                                span = span.with_lo(prev_span.hi());
                             }
                         }
-                        prev = Some(pred.span);
+                        prev_span = Some(pred.span);
                     }
                     span
                 };
@@ -1674,10 +1689,10 @@ pub fn check_crate(
     let mut validator = AstValidator {
         sess,
         features,
-        extern_mod: None,
+        extern_mod_span: None,
         outer_trait_or_trait_impl: None,
         has_proc_macro_decls: false,
-        outer_impl_trait: None,
+        outer_impl_trait_span: None,
         disallow_tilde_const: Some(TildeConstReason::Item),
         extern_mod_safety: None,
         lint_buffer: lints,
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 0eb2043eaa35..6f9737e08314 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -2,7 +2,7 @@
 
 use rustc_ast::ParamKindOrd;
 use rustc_errors::codes::*;
-use rustc_errors::{Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
+use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{Ident, Span, Symbol};
 
@@ -394,11 +394,7 @@ pub(crate) struct EmptyLabelManySpans(pub Vec<Span>);
 
 // The derive for `Vec<Span>` does multiple calls to `span_label`, adding commas between each
 impl Subdiagnostic for EmptyLabelManySpans {
-    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
-        self,
-        diag: &mut Diag<'_, G>,
-        _: &F,
-    ) {
+    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
         diag.span_labels(self.0, "");
     }
 }
@@ -535,7 +531,6 @@ pub(crate) struct WhereClauseBeforeTypeAlias {
 }
 
 #[derive(Subdiagnostic)]
-
 pub(crate) enum WhereClauseBeforeTypeAliasSugg {
     #[suggestion(ast_passes_remove_suggestion, applicability = "machine-applicable", code = "")]
     Remove {
@@ -732,13 +727,6 @@ pub(crate) struct AssociatedSuggestion2 {
     pub potential_assoc: Ident,
 }
 
-#[derive(Diagnostic)]
-#[diag(ast_passes_stability_outside_std, code = E0734)]
-pub(crate) struct StabilityOutsideStd {
-    #[primary_span]
-    pub span: Span,
-}
-
 #[derive(Diagnostic)]
 #[diag(ast_passes_feature_on_non_nightly, code = E0554)]
 pub(crate) struct FeatureOnNonNightly {
@@ -757,11 +745,7 @@ pub(crate) struct StableFeature {
 }
 
 impl Subdiagnostic for StableFeature {
-    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
-        self,
-        diag: &mut Diag<'_, G>,
-        _: &F,
-    ) {
+    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
         diag.arg("name", self.name);
         diag.arg("since", self.since);
         diag.help(fluent::ast_passes_stable_since);
@@ -804,7 +788,14 @@ pub(crate) struct NegativeBoundWithParentheticalNotation {
 pub(crate) struct MatchArmWithNoBody {
     #[primary_span]
     pub span: Span,
-    #[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
+    // We include the braces around `todo!()` so that a comma is optional, and we don't have to have
+    // any logic looking at the arm being replaced if there was a comma already or not for the
+    // resulting code to be correct.
+    #[suggestion(
+        code = " => {{ todo!() }}",
+        applicability = "has-placeholders",
+        style = "verbose"
+    )]
     pub suggestion: Span,
 }
 
@@ -824,3 +815,12 @@ pub(crate) struct DuplicatePreciseCapturing {
     #[label]
     pub bound2: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_extern_without_abi)]
+#[help]
+pub(crate) struct MissingAbi {
+    #[primary_span]
+    #[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")]
+    pub span: Span,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 80b99f944856..e312f15f05b0 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -1,12 +1,11 @@
 use rustc_ast as ast;
 use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
 use rustc_ast::{NodeId, PatKind, attr, token};
-use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue};
+use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
 use rustc_session::Session;
-use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
+use rustc_session::parse::{feature_err, feature_warn};
 use rustc_span::source_map::Spanned;
 use rustc_span::{Span, Symbol, sym};
-use rustc_target::spec::abi;
 use thin_vec::ThinVec;
 
 use crate::errors;
@@ -73,35 +72,6 @@ struct PostExpansionVisitor<'a> {
 }
 
 impl<'a> PostExpansionVisitor<'a> {
-    #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
-    fn check_abi(&self, abi: ast::StrLit) {
-        let ast::StrLit { symbol_unescaped, span, .. } = abi;
-
-        match abi::is_enabled(self.features, span, symbol_unescaped.as_str()) {
-            Ok(()) => (),
-            Err(abi::AbiDisabled::Unstable { feature, explain }) => {
-                feature_err_issue(&self.sess, feature, span, GateIssue::Language, explain).emit();
-            }
-            Err(abi::AbiDisabled::Unrecognized) => {
-                if self.sess.opts.pretty.is_none_or(|ppm| ppm.needs_hir()) {
-                    self.sess.dcx().span_delayed_bug(
-                        span,
-                        format!(
-                            "unrecognized ABI not caught in lowering: {}",
-                            symbol_unescaped.as_str()
-                        ),
-                    );
-                }
-            }
-        }
-    }
-
-    fn check_extern(&self, ext: ast::Extern) {
-        if let ast::Extern::Explicit(abi, _) = ext {
-            self.check_abi(abi);
-        }
-    }
-
     /// Feature gate `impl Trait` inside `type Alias = $type_expr;`.
     fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) {
         struct ImplTraitVisitor<'a> {
@@ -129,6 +99,13 @@ impl<'a> PostExpansionVisitor<'a> {
                 }
                 visit::walk_ty(self, ty);
             }
+
+            fn visit_anon_const(&mut self, _: &ast::AnonConst) -> Self::Result {
+                // We don't walk the anon const because it crosses a conceptual boundary: We're no
+                // longer "inside" the original type.
+                // Brittle: We assume that the callers of `check_impl_trait` will later recurse into
+                // the items found in the AnonConst to look for nested TyAliases.
+            }
         }
         ImplTraitVisitor { vis: self, in_associated_ty }.visit_ty(ty);
     }
@@ -208,28 +185,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 );
             }
         }
-
-        // Emit errors for non-staged-api crates.
-        if !self.features.staged_api() {
-            if attr.has_name(sym::unstable)
-                || attr.has_name(sym::stable)
-                || attr.has_name(sym::rustc_const_unstable)
-                || attr.has_name(sym::rustc_const_stable)
-                || attr.has_name(sym::rustc_default_body_unstable)
-            {
-                self.sess.dcx().emit_err(errors::StabilityOutsideStd { span: attr.span });
-            }
-        }
     }
 
     fn visit_item(&mut self, i: &'a ast::Item) {
         match &i.kind {
-            ast::ItemKind::ForeignMod(foreign_module) => {
-                if let Some(abi) = foreign_module.abi {
-                    self.check_abi(abi);
-                }
+            ast::ItemKind::ForeignMod(_foreign_module) => {
+                // handled during lowering
             }
-
             ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..) => {
                 for attr in attr::filter_by_name(&i.attrs, sym::repr) {
                     for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) {
@@ -274,7 +236,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 gate!(&self, trait_alias, i.span, "trait aliases are experimental");
             }
 
-            ast::ItemKind::MacroDef(ast::MacroDef { macro_rules: false, .. }) => {
+            ast::ItemKind::MacroDef(_, ast::MacroDef { macro_rules: false, .. }) => {
                 let msg = "`macro` is experimental";
                 gate!(&self, decl_macro, i.span, msg);
             }
@@ -316,7 +278,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         match &ty.kind {
             ast::TyKind::BareFn(bare_fn_ty) => {
                 // Function pointers cannot be `const`
-                self.check_extern(bare_fn_ty.ext);
                 self.check_late_bound_lifetime_defs(&bare_fn_ty.generic_params);
             }
             ast::TyKind::Never => {
@@ -371,17 +332,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             ast::ExprKind::TryBlock(_) => {
                 gate!(&self, try_blocks, e.span, "`try` expression is experimental");
             }
-            ast::ExprKind::Lit(token::Lit { kind: token::LitKind::Float, suffix, .. }) => {
-                match suffix {
-                    Some(sym::f16) => {
-                        gate!(&self, f16, e.span, "the type `f16` is unstable")
-                    }
-                    Some(sym::f128) => {
-                        gate!(&self, f128, e.span, "the type `f128` is unstable")
-                    }
-                    _ => (),
+            ast::ExprKind::Lit(token::Lit {
+                kind: token::LitKind::Float | token::LitKind::Integer,
+                suffix,
+                ..
+            }) => match suffix {
+                Some(sym::f16) => {
+                    gate!(&self, f16, e.span, "the type `f16` is unstable")
                 }
-            }
+                Some(sym::f128) => {
+                    gate!(&self, f128, e.span, "the type `f128` is unstable")
+                }
+                _ => (),
+            },
             _ => {}
         }
         visit::walk_expr(self, e)
@@ -419,9 +382,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 
     fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
-        if let Some(header) = fn_kind.header() {
+        if let Some(_header) = fn_kind.header() {
             // Stability of const fn methods are covered in `visit_assoc_item` below.
-            self.check_extern(header.ext);
         }
 
         if let FnKind::Closure(ast::ClosureBinder::For { generic_params, .. }, ..) = fn_kind {
@@ -530,12 +492,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
         half_open_range_patterns_in_slices,
         "half-open range patterns in slices are unstable"
     );
-    gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
     gate_all!(associated_const_equality, "associated const equality is incomplete");
     gate_all!(yeet_expr, "`do yeet` expression is experimental");
     gate_all!(dyn_star, "`dyn*` trait objects are experimental");
     gate_all!(const_closures, "const closures are experimental");
     gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
+    gate_all!(ergonomic_clones, "ergonomic clones are experimental");
     gate_all!(explicit_tail_calls, "`become` expression is experimental");
     gate_all!(generic_const_items, "generic const items are experimental");
     gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
@@ -548,6 +510,10 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
     gate_all!(unsafe_fields, "`unsafe` fields are experimental");
     gate_all!(unsafe_binders, "unsafe binder types are experimental");
+    gate_all!(contracts, "contracts are incomplete");
+    gate_all!(contracts_internals, "contract internal machinery is for internal use only");
+    gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
+    gate_all!(super_let, "`super let` is experimental");
 
     if !visitor.features.never_patterns() {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs
index b4ed70d83e57..093199cf3421 100644
--- a/compiler/rustc_ast_passes/src/lib.rs
+++ b/compiler/rustc_ast_passes/src/lib.rs
@@ -10,7 +10,6 @@
 #![feature(iter_is_partitioned)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 pub mod ast_validation;
diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml
index f290fedcd8b9..b704040be961 100644
--- a/compiler/rustc_ast_pretty/Cargo.toml
+++ b/compiler/rustc_ast_pretty/Cargo.toml
@@ -1,14 +1,17 @@
 [package]
 name = "rustc_ast_pretty"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
 itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
-rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_span = { path = "../rustc_span" }
+# tidy-alphabetical-end
+
+[dev-dependencies]
+# tidy-alphabetical-start
 thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs
index 602ab69ee5b5..84d9ce278a21 100644
--- a/compiler/rustc_ast_pretty/src/lib.rs
+++ b/compiler/rustc_ast_pretty/src/lib.rs
@@ -3,7 +3,6 @@
 #![doc(rust_logo)]
 #![feature(box_patterns)]
 #![feature(rustdoc_internals)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod helpers;
diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs
index 97cb6e52d562..551506f2aef8 100644
--- a/compiler/rustc_ast_pretty/src/pprust/mod.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs
@@ -5,14 +5,10 @@ pub mod state;
 use std::borrow::Cow;
 
 use rustc_ast as ast;
-use rustc_ast::token::{Nonterminal, Token, TokenKind};
+use rustc_ast::token::{Token, TokenKind};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 pub use state::{AnnNode, Comments, PpAnn, PrintState, State, print_crate};
 
-pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
-    State::new().nonterminal_to_string(nt)
-}
-
 /// Print the token kind precisely, without converting `$crate` into its respective crate name.
 pub fn token_kind_to_string(tok: &TokenKind) -> Cow<'static, str> {
     State::new().token_kind_to_string(tok)
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 9b958ed6b0d6..0985ebf945bb 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -7,12 +7,11 @@ mod fixup;
 mod item;
 
 use std::borrow::Cow;
+use std::sync::Arc;
 
 use rustc_ast::attr::AttrIdGenerator;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{
-    self, BinOpToken, CommentKind, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind,
-};
+use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
 use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{Comment, CommentStyle};
@@ -21,12 +20,10 @@ use rustc_ast::{
     InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind,
     RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr,
 };
-use rustc_data_structures::sync::Lrc;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{SourceMap, Spanned};
 use rustc_span::symbol::IdentPrinter;
 use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym};
-use thin_vec::ThinVec;
 
 use crate::pp::Breaks::{Consistent, Inconsistent};
 use crate::pp::{self, Breaks};
@@ -106,7 +103,7 @@ fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec<String> {
 fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comment> {
     let sm = SourceMap::new(sm.path_mapping().clone());
     let source_file = sm.new_source_file(path, src);
-    let text = Lrc::clone(&(*source_file.src.as_ref().unwrap()));
+    let text = Arc::clone(&(*source_file.src.as_ref().unwrap()));
 
     let text: &str = text.as_str();
     let start_bpos = source_file.start_pos;
@@ -319,7 +316,7 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
         (tt1, Tok(Token { kind: Comma | Semi | Dot, .. }, _)) if !is_punct(tt1) => false,
 
         // IDENT + `!`: `println!()`, but `if !x { ... }` needs a space after the `if`
-        (Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Not, .. }, _))
+        (Tok(Token { kind: Ident(sym, is_raw), span }, _), Tok(Token { kind: Bang, .. }, _))
             if !Ident::new(*sym, *span).is_reserved() || matches!(is_raw, IdentIsRaw::Yes) =>
         {
             false
@@ -344,21 +341,6 @@ fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
     }
 }
 
-fn binop_to_string(op: BinOpToken) -> &'static str {
-    match op {
-        token::Plus => "+",
-        token::Minus => "-",
-        token::Star => "*",
-        token::Slash => "/",
-        token::Percent => "%",
-        token::Caret => "^",
-        token::And => "&",
-        token::Or => "|",
-        token::Shl => "<<",
-        token::Shr => ">>",
-    }
-}
-
 pub fn doc_comment_to_string(
     comment_kind: CommentKind,
     attr_style: ast::AttrStyle,
@@ -424,20 +406,23 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         self.ann_post(ident)
     }
 
-    fn strsep<T, F>(
+    fn strsep<'x, T: 'x, F, I>(
         &mut self,
         sep: &'static str,
         space_before: bool,
         b: Breaks,
-        elts: &[T],
+        elts: I,
         mut op: F,
     ) where
         F: FnMut(&mut Self, &T),
+        I: IntoIterator<Item = &'x T>,
     {
+        let mut it = elts.into_iter();
+
         self.rbox(0, b);
-        if let Some((first, rest)) = elts.split_first() {
+        if let Some(first) = it.next() {
             op(self, first);
-            for elt in rest {
+            for elt in it {
                 if space_before {
                     self.space();
                 }
@@ -448,9 +433,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         self.end();
     }
 
-    fn commasep<T, F>(&mut self, b: Breaks, elts: &[T], op: F)
+    fn commasep<'x, T: 'x, F, I>(&mut self, b: Breaks, elts: I, op: F)
     where
         F: FnMut(&mut Self, &T),
+        I: IntoIterator<Item = &'x T>,
     {
         self.strsep(",", false, b, elts, op)
     }
@@ -592,11 +578,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         let mut printed = false;
         for attr in attrs {
             if attr.style == kind {
-                self.print_attribute_inline(attr, is_inline);
-                if is_inline {
-                    self.nbsp();
+                if self.print_attribute_inline(attr, is_inline) {
+                    if is_inline {
+                        self.nbsp();
+                    }
+                    printed = true;
                 }
-                printed = true;
             }
         }
         if printed && trailing_hardbreak && !is_inline {
@@ -605,7 +592,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         printed
     }
 
-    fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) {
+    fn print_attribute_inline(&mut self, attr: &ast::Attribute, is_inline: bool) -> bool {
+        if attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) {
+            // It's not a valid identifier, so avoid printing it
+            // to keep the printed code reasonably parse-able.
+            return false;
+        }
         if !is_inline {
             self.hardbreak_if_not_bol();
         }
@@ -624,6 +616,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 self.hardbreak()
             }
         }
+        true
     }
 
     fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
@@ -883,14 +876,6 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
     }
 
-    fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
-        // We extract the token stream from the AST fragment and pretty print
-        // it, rather than using AST pretty printing, because `Nonterminal` is
-        // slated for removal in #124141. (This method will also then be
-        // removed.)
-        self.tts_to_string(&TokenStream::from_nonterminal_ast(nt))
-    }
-
     /// Print the token kind precisely, without converting `$crate` into its respective crate name.
     fn token_kind_to_string(&self, tok: &TokenKind) -> Cow<'static, str> {
         self.token_kind_to_string_ext(tok, None)
@@ -909,12 +894,30 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
             token::Ne => "!=".into(),
             token::Ge => ">=".into(),
             token::Gt => ">".into(),
-            token::Not => "!".into(),
+            token::Bang => "!".into(),
             token::Tilde => "~".into(),
             token::OrOr => "||".into(),
             token::AndAnd => "&&".into(),
-            token::BinOp(op) => binop_to_string(op).into(),
-            token::BinOpEq(op) => format!("{}=", binop_to_string(op)).into(),
+            token::Plus => "+".into(),
+            token::Minus => "-".into(),
+            token::Star => "*".into(),
+            token::Slash => "/".into(),
+            token::Percent => "%".into(),
+            token::Caret => "^".into(),
+            token::And => "&".into(),
+            token::Or => "|".into(),
+            token::Shl => "<<".into(),
+            token::Shr => ">>".into(),
+            token::PlusEq => "+=".into(),
+            token::MinusEq => "-=".into(),
+            token::StarEq => "*=".into(),
+            token::SlashEq => "/=".into(),
+            token::PercentEq => "%=".into(),
+            token::CaretEq => "^=".into(),
+            token::AndEq => "&=".into(),
+            token::OrEq => "|=".into(),
+            token::ShlEq => "<<=".into(),
+            token::ShrEq => ">>=".into(),
 
             /* Structural symbols */
             token::At => "@".into(),
@@ -965,8 +968,6 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 doc_comment_to_string(comment_kind, attr_style, data).into()
             }
             token::Eof => "<eof>".into(),
-
-            token::Interpolated(ref nt) => self.nonterminal_to_string(&nt).into(),
         }
     }
 
@@ -1148,6 +1149,28 @@ impl<'a> State<'a> {
         }
     }
 
+    pub fn print_ty_pat(&mut self, pat: &ast::TyPat) {
+        match &pat.kind {
+            rustc_ast::TyPatKind::Range(start, end, include_end) => {
+                if let Some(start) = start {
+                    self.print_expr_anon_const(start, &[]);
+                }
+                self.word("..");
+                if let Some(end) = end {
+                    if let RangeEnd::Included(_) = include_end.node {
+                        self.word("=");
+                    }
+                    self.print_expr_anon_const(end, &[]);
+                }
+            }
+            rustc_ast::TyPatKind::Err(_) => {
+                self.popen();
+                self.word("/*ERROR*/");
+                self.pclose();
+            }
+        }
+    }
+
     pub fn print_type(&mut self, ty: &ast::Ty) {
         self.maybe_print_comment(ty.span.lo());
         self.ibox(0);
@@ -1252,7 +1275,7 @@ impl<'a> State<'a> {
             ast::TyKind::Pat(ty, pat) => {
                 self.print_type(ty);
                 self.word(" is ");
-                self.print_pat(pat);
+                self.print_ty_pat(pat);
             }
         }
         self.end();
@@ -1303,6 +1326,9 @@ impl<'a> State<'a> {
                 self.print_outer_attributes(&loc.attrs);
                 self.space_if_not_bol();
                 self.ibox(INDENT_UNIT);
+                if loc.super_.is_some() {
+                    self.word_nbsp("super");
+                }
                 self.word_nbsp("let");
 
                 self.ibox(INDENT_UNIT);
@@ -1589,9 +1615,9 @@ impl<'a> State<'a> {
     fn print_pat(&mut self, pat: &ast::Pat) {
         self.maybe_print_comment(pat.span.lo());
         self.ann.pre(self, AnnNode::Pat(pat));
-        /* Pat isn't normalized, but the beauty of it
-        is that it doesn't matter */
+        /* Pat isn't normalized, but the beauty of it is that it doesn't matter */
         match &pat.kind {
+            PatKind::Missing => unreachable!(),
             PatKind::Wild => self.word("_"),
             PatKind::Never => self.word("!"),
             PatKind::Ident(BindingMode(by_ref, mutbl), ident, sub) => {
@@ -1756,6 +1782,13 @@ impl<'a> State<'a> {
                 self.print_mutability(*m, false);
                 self.word("self")
             }
+            SelfKind::Pinned(lt, m) => {
+                self.word("&");
+                self.print_opt_lifetime(lt);
+                self.word("pin ");
+                self.print_mutability(*m, true);
+                self.word("self")
+            }
             SelfKind::Explicit(typ, m) => {
                 self.print_mutability(*m, false);
                 self.word("self");
@@ -1906,12 +1939,7 @@ impl<'a> State<'a> {
                 if let Some(eself) = input.to_self() {
                     self.print_explicit_self(&eself);
                 } else {
-                    let invalid = if let PatKind::Ident(_, ident, _) = input.pat.kind {
-                        ident.name == kw::Empty
-                    } else {
-                        false
-                    };
-                    if !invalid {
+                    if !matches!(input.pat.kind, PatKind::Missing) {
                         self.print_pat(&input.pat);
                         self.word(":");
                         self.space();
@@ -1944,15 +1972,7 @@ impl<'a> State<'a> {
     ) {
         self.ibox(INDENT_UNIT);
         self.print_formal_generic_params(generic_params);
-        let generics = ast::Generics {
-            params: ThinVec::new(),
-            where_clause: ast::WhereClause {
-                has_where_token: false,
-                predicates: ThinVec::new(),
-                span: DUMMY_SP,
-            },
-            span: DUMMY_SP,
-        };
+        let generics = ast::Generics::default();
         let header = ast::FnHeader { safety, ext, ..ast::FnHeader::default() };
         self.print_fn(decl, header, name, &generics);
         self.end();
@@ -2022,7 +2042,7 @@ impl<'a> State<'a> {
     }
 
     fn print_attribute(&mut self, attr: &ast::Attribute) {
-        self.print_attribute_inline(attr, false)
+        self.print_attribute_inline(attr, false);
     }
 
     fn print_meta_list_item(&mut self, item: &ast::MetaItemInner) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 4b1374ceef31..df848a26d390 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -5,10 +5,10 @@ use itertools::{Itertools, Position};
 use rustc_ast::ptr::P;
 use rustc_ast::util::classify;
 use rustc_ast::util::literal::escape_byte_str_symbol;
-use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity};
+use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
 use rustc_ast::{
     self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
-    FormatDebugHex, FormatSign, FormatTrait, token,
+    FormatDebugHex, FormatSign, FormatTrait, YieldKind, token,
 };
 
 use crate::pp::Breaks::Inconsistent;
@@ -274,23 +274,22 @@ impl<'a> State<'a> {
 
     fn print_expr_binary(
         &mut self,
-        op: ast::BinOp,
+        op: ast::BinOpKind,
         lhs: &ast::Expr,
         rhs: &ast::Expr,
         fixup: FixupContext,
     ) {
-        let assoc_op = AssocOp::from_ast_binop(op.node);
-        let binop_prec = assoc_op.precedence();
+        let binop_prec = op.precedence();
         let left_prec = lhs.precedence();
         let right_prec = rhs.precedence();
 
-        let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() {
+        let (mut left_needs_paren, right_needs_paren) = match op.fixity() {
             Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec),
             Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec),
             Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec),
         };
 
-        match (&lhs.kind, op.node) {
+        match (&lhs.kind, op) {
             // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is
             // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead
             // of `(x as i32) < ...`. We need to convince it _not_ to do that.
@@ -313,7 +312,7 @@ impl<'a> State<'a> {
 
         self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
         self.space();
-        self.word_space(op.node.as_str());
+        self.word_space(op.as_str());
         self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression());
     }
 
@@ -411,7 +410,7 @@ impl<'a> State<'a> {
                 self.print_expr_method_call(seg, receiver, args, fixup);
             }
             ast::ExprKind::Binary(op, lhs, rhs) => {
-                self.print_expr_binary(*op, lhs, rhs, fixup);
+                self.print_expr_binary(op.node, lhs, rhs, fixup);
             }
             ast::ExprKind::Unary(op, expr) => {
                 self.print_expr_unary(*op, expr, fixup);
@@ -575,6 +574,14 @@ impl<'a> State<'a> {
                 );
                 self.word(".await");
             }
+            ast::ExprKind::Use(expr, _) => {
+                self.print_expr_cond_paren(
+                    expr,
+                    expr.precedence() < ExprPrecedence::Unambiguous,
+                    fixup,
+                );
+                self.word(".use");
+            }
             ast::ExprKind::Assign(lhs, rhs, _) => {
                 self.print_expr_cond_paren(
                     lhs,
@@ -598,8 +605,7 @@ impl<'a> State<'a> {
                     fixup.leftmost_subexpression(),
                 );
                 self.space();
-                self.word(op.node.as_str());
-                self.word_space("=");
+                self.word_space(op.node.as_str());
                 self.print_expr_cond_paren(
                     rhs,
                     rhs.precedence() < ExprPrecedence::Assign,
@@ -754,7 +760,7 @@ impl<'a> State<'a> {
                 self.print_expr(e, FixupContext::default());
                 self.pclose();
             }
-            ast::ExprKind::Yield(e) => {
+            ast::ExprKind::Yield(YieldKind::Prefix(e)) => {
                 self.word("yield");
 
                 if let Some(expr) = e {
@@ -766,6 +772,14 @@ impl<'a> State<'a> {
                     );
                 }
             }
+            ast::ExprKind::Yield(YieldKind::Postfix(e)) => {
+                self.print_expr_cond_paren(
+                    e,
+                    e.precedence() < ExprPrecedence::Unambiguous,
+                    fixup.leftmost_subexpression_with_dot(),
+                );
+                self.word(".yield");
+            }
             ast::ExprKind::Try(e) => {
                 self.print_expr_cond_paren(
                     e,
@@ -886,6 +900,7 @@ impl<'a> State<'a> {
     fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) {
         match capture_clause {
             ast::CaptureBy::Value { .. } => self.word_space("move"),
+            ast::CaptureBy::Use { .. } => self.word_space("use"),
             ast::CaptureBy::Ref => {}
         }
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 897c275d850c..653bd77cc4dd 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -28,36 +28,43 @@ impl<'a> State<'a> {
     }
 
     fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
-        let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+        let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(span.lo());
         self.print_outer_attributes(attrs);
         match kind {
-            ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
-                self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
-            }
-            ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => {
-                self.print_item_const(
-                    ident,
-                    Some(*mutability),
-                    &ast::Generics::default(),
-                    ty,
-                    expr.as_deref(),
-                    vis,
-                    *safety,
-                    ast::Defaultness::Final,
-                )
+            ast::ForeignItemKind::Fn(func) => {
+                self.print_fn_full(vis, attrs, &*func);
             }
+            ast::ForeignItemKind::Static(box ast::StaticItem {
+                ident,
+                ty,
+                mutability,
+                expr,
+                safety,
+                define_opaque,
+            }) => self.print_item_const(
+                *ident,
+                Some(*mutability),
+                &ast::Generics::default(),
+                ty,
+                expr.as_deref(),
+                vis,
+                *safety,
+                ast::Defaultness::Final,
+                define_opaque.as_deref(),
+            ),
             ast::ForeignItemKind::TyAlias(box ast::TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 self.print_associated_type(
-                    ident,
+                    *ident,
                     generics,
                     *where_clauses,
                     bounds,
@@ -86,7 +93,9 @@ impl<'a> State<'a> {
         vis: &ast::Visibility,
         safety: ast::Safety,
         defaultness: ast::Defaultness,
+        define_opaque: Option<&[(ast::NodeId, ast::Path)]>,
     ) {
+        self.print_define_opaques(define_opaque);
         self.head("");
         self.print_visibility(vis);
         self.print_safety(safety);
@@ -155,7 +164,7 @@ impl<'a> State<'a> {
         self.print_outer_attributes(&item.attrs);
         self.ann.pre(self, AnnNode::Item(item));
         match &item.kind {
-            ast::ItemKind::ExternCrate(orig_name) => {
+            ast::ItemKind::ExternCrate(orig_name, ident) => {
                 self.head(visibility_qualified(&item.vis, "extern crate"));
                 if let &Some(orig_name) = orig_name {
                     self.print_name(orig_name);
@@ -163,7 +172,7 @@ impl<'a> State<'a> {
                     self.word("as");
                     self.space();
                 }
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
                 self.word(";");
                 self.end(); // end inner head-block
                 self.end(); // end outer head-block
@@ -174,10 +183,17 @@ impl<'a> State<'a> {
                 self.print_use_tree(tree);
                 self.word(";");
             }
-            ast::ItemKind::Static(box StaticItem { ty, safety, mutability: mutbl, expr: body }) => {
+            ast::ItemKind::Static(box StaticItem {
+                ident,
+                ty,
+                safety,
+                mutability: mutbl,
+                expr: body,
+                define_opaque,
+            }) => {
                 self.print_safety(*safety);
                 self.print_item_const(
-                    item.ident,
+                    *ident,
                     Some(*mutbl),
                     &ast::Generics::default(),
                     ty,
@@ -185,11 +201,19 @@ impl<'a> State<'a> {
                     &item.vis,
                     ast::Safety::Default,
                     ast::Defaultness::Final,
+                    define_opaque.as_deref(),
                 );
             }
-            ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
+            ast::ItemKind::Const(box ast::ConstItem {
+                defaultness,
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+            }) => {
                 self.print_item_const(
-                    item.ident,
+                    *ident,
                     None,
                     generics,
                     ty,
@@ -197,26 +221,19 @@ impl<'a> State<'a> {
                     &item.vis,
                     ast::Safety::Default,
                     *defaultness,
+                    define_opaque.as_deref(),
                 );
             }
-            ast::ItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
-                self.print_fn_full(
-                    sig,
-                    item.ident,
-                    generics,
-                    &item.vis,
-                    *defaultness,
-                    body.as_deref(),
-                    &item.attrs,
-                );
+            ast::ItemKind::Fn(func) => {
+                self.print_fn_full(&item.vis, &item.attrs, &*func);
             }
-            ast::ItemKind::Mod(safety, mod_kind) => {
+            ast::ItemKind::Mod(safety, ident, mod_kind) => {
                 self.head(Self::to_string(|s| {
                     s.print_visibility(&item.vis);
                     s.print_safety(*safety);
                     s.word("mod");
                 }));
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
 
                 match mod_kind {
                     ModKind::Loaded(items, ..) => {
@@ -260,13 +277,14 @@ impl<'a> State<'a> {
             }
             ast::ItemKind::TyAlias(box ast::TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 self.print_associated_type(
-                    item.ident,
+                    *ident,
                     generics,
                     *where_clauses,
                     bounds,
@@ -275,16 +293,16 @@ impl<'a> State<'a> {
                     *defaultness,
                 );
             }
-            ast::ItemKind::Enum(enum_definition, params) => {
-                self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis);
+            ast::ItemKind::Enum(ident, enum_definition, params) => {
+                self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis);
             }
-            ast::ItemKind::Struct(struct_def, generics) => {
+            ast::ItemKind::Struct(ident, struct_def, generics) => {
                 self.head(visibility_qualified(&item.vis, "struct"));
-                self.print_struct(struct_def, generics, item.ident, item.span, true);
+                self.print_struct(struct_def, generics, *ident, item.span, true);
             }
-            ast::ItemKind::Union(struct_def, generics) => {
+            ast::ItemKind::Union(ident, struct_def, generics) => {
                 self.head(visibility_qualified(&item.vis, "union"));
-                self.print_struct(struct_def, generics, item.ident, item.span, true);
+                self.print_struct(struct_def, generics, *ident, item.span, true);
             }
             ast::ItemKind::Impl(box ast::Impl {
                 safety,
@@ -334,19 +352,19 @@ impl<'a> State<'a> {
                 self.bclose(item.span, empty);
             }
             ast::ItemKind::Trait(box ast::Trait {
-                is_auto,
                 safety,
+                is_auto,
+                ident,
                 generics,
                 bounds,
                 items,
-                ..
             }) => {
                 self.head("");
                 self.print_visibility(&item.vis);
                 self.print_safety(*safety);
                 self.print_is_auto(*is_auto);
                 self.word_nbsp("trait");
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
                 self.print_generic_params(&generics.params);
                 if !bounds.is_empty() {
                     self.word_nbsp(":");
@@ -362,9 +380,9 @@ impl<'a> State<'a> {
                 let empty = item.attrs.is_empty() && items.is_empty();
                 self.bclose(item.span, empty);
             }
-            ast::ItemKind::TraitAlias(generics, bounds) => {
+            ast::ItemKind::TraitAlias(ident, generics, bounds) => {
                 self.head(visibility_qualified(&item.vis, "trait"));
-                self.print_ident(item.ident);
+                self.print_ident(*ident);
                 self.print_generic_params(&generics.params);
                 self.nbsp();
                 if !bounds.is_empty() {
@@ -382,8 +400,8 @@ impl<'a> State<'a> {
                     self.word(";");
                 }
             }
-            ast::ItemKind::MacroDef(macro_def) => {
-                self.print_mac_def(macro_def, &item.ident, item.span, |state| {
+            ast::ItemKind::MacroDef(ident, macro_def) => {
+                self.print_mac_def(macro_def, &ident, item.span, |state| {
                     state.print_visibility(&item.vis)
                 });
             }
@@ -536,18 +554,25 @@ impl<'a> State<'a> {
     }
 
     fn print_assoc_item(&mut self, item: &ast::AssocItem) {
-        let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item;
+        let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
         self.ann.pre(self, AnnNode::SubItem(id));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(span.lo());
         self.print_outer_attributes(attrs);
         match kind {
-            ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
-                self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
+            ast::AssocItemKind::Fn(func) => {
+                self.print_fn_full(vis, attrs, &*func);
             }
-            ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
+            ast::AssocItemKind::Const(box ast::ConstItem {
+                defaultness,
+                ident,
+                generics,
+                ty,
+                expr,
+                define_opaque,
+            }) => {
                 self.print_item_const(
-                    ident,
+                    *ident,
                     None,
                     generics,
                     ty,
@@ -555,17 +580,19 @@ impl<'a> State<'a> {
                     vis,
                     ast::Safety::Default,
                     *defaultness,
+                    define_opaque.as_deref(),
                 );
             }
             ast::AssocItemKind::Type(box ast::TyAlias {
                 defaultness,
+                ident,
                 generics,
                 where_clauses,
                 bounds,
                 ty,
             }) => {
                 self.print_associated_type(
-                    ident,
+                    *ident,
                     generics,
                     *where_clauses,
                     bounds,
@@ -651,22 +678,21 @@ impl<'a> State<'a> {
         }
     }
 
-    fn print_fn_full(
-        &mut self,
-        sig: &ast::FnSig,
-        name: Ident,
-        generics: &ast::Generics,
-        vis: &ast::Visibility,
-        defaultness: ast::Defaultness,
-        body: Option<&ast::Block>,
-        attrs: &[ast::Attribute],
-    ) {
+    fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) {
+        let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque } = func;
+
+        self.print_define_opaques(define_opaque.as_deref());
+
         if body.is_some() {
             self.head("");
         }
         self.print_visibility(vis);
-        self.print_defaultness(defaultness);
-        self.print_fn(&sig.decl, sig.header, Some(name), generics);
+        self.print_defaultness(*defaultness);
+        self.print_fn(&sig.decl, sig.header, Some(*ident), generics);
+        if let Some(contract) = &contract {
+            self.nbsp();
+            self.print_contract(contract);
+        }
         if let Some(body) = body {
             self.nbsp();
             self.print_block_with_attrs(body, attrs);
@@ -675,21 +701,51 @@ impl<'a> State<'a> {
         }
     }
 
+    fn print_define_opaques(&mut self, define_opaque: Option<&[(ast::NodeId, ast::Path)]>) {
+        if let Some(define_opaque) = define_opaque {
+            self.word("#[define_opaque(");
+            for (i, (_, path)) in define_opaque.iter().enumerate() {
+                if i != 0 {
+                    self.word_space(",");
+                }
+
+                self.print_path(path, false, 0);
+            }
+            self.word(")]");
+        }
+        self.hardbreak_if_not_bol();
+    }
+
+    fn print_contract(&mut self, contract: &ast::FnContract) {
+        if let Some(pred) = &contract.requires {
+            self.word("rustc_requires");
+            self.popen();
+            self.print_expr(pred, FixupContext::default());
+            self.pclose();
+        }
+        if let Some(pred) = &contract.ensures {
+            self.word("rustc_ensures");
+            self.popen();
+            self.print_expr(pred, FixupContext::default());
+            self.pclose();
+        }
+    }
+
     pub(crate) fn print_fn(
         &mut self,
         decl: &ast::FnDecl,
         header: ast::FnHeader,
-        name: Option<Ident>,
+        ident: Option<Ident>,
         generics: &ast::Generics,
     ) {
         self.print_fn_header_info(header);
-        if let Some(name) = name {
+        if let Some(ident) = ident {
             self.nbsp();
-            self.print_ident(name);
+            self.print_ident(ident);
         }
         self.print_generic_params(&generics.params);
         self.print_fn_params_and_ret(decl, false);
-        self.print_where_clause(&generics.where_clause)
+        self.print_where_clause(&generics.where_clause);
     }
 
     pub(crate) fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) {
@@ -726,7 +782,8 @@ impl<'a> State<'a> {
     }
 
     pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
-        let ast::WherePredicate { kind, id: _, span: _ } = predicate;
+        let ast::WherePredicate { attrs, kind, id: _, span: _, is_placeholder: _ } = predicate;
+        self.print_outer_attributes(attrs);
         match kind {
             ast::WherePredicateKind::BoundPredicate(where_bound_predicate) => {
                 self.print_where_bound_predicate(where_bound_predicate);
diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs
index 4c42dd1f2023..bc7f22766a5c 100644
--- a/compiler/rustc_ast_pretty/src/pprust/tests.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs
@@ -7,12 +7,12 @@ use super::*;
 fn fun_to_string(
     decl: &ast::FnDecl,
     header: ast::FnHeader,
-    name: Ident,
+    ident: Ident,
     generics: &ast::Generics,
 ) -> String {
     to_string(|s| {
         s.head("");
-        s.print_fn(decl, header, Some(name), generics);
+        s.print_fn(decl, header, Some(ident), generics);
         s.end(); // Close the head box.
         s.end(); // Close the outer box.
     })
diff --git a/compiler/rustc_attr_data_structures/Cargo.toml b/compiler/rustc_attr_data_structures/Cargo.toml
index 2ee58f24470e..b18923c337ff 100644
--- a/compiler/rustc_attr_data_structures/Cargo.toml
+++ b/compiler/rustc_attr_data_structures/Cargo.toml
@@ -1,20 +1,16 @@
 [package]
 name = "rustc_attr_data_structures"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc_abi = { path = "../rustc_abi" }
-rustc_ast = { path = "../rustc_ast" }
-rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_data_structures = { path = "../rustc_data_structures" }
-rustc_errors = { path = "../rustc_errors" }
-rustc_feature = { path = "../rustc_feature" }
-rustc_fluent_macro = { path = "../rustc_fluent_macro" }
-rustc_lexer = { path = "../rustc_lexer" }
-rustc_macros = { path = "../rustc_macros" }
-rustc_serialize = { path = "../rustc_serialize" }
-rustc_session = { path = "../rustc_session" }
-rustc_span = { path = "../rustc_span" }
+rustc_abi = {path = "../rustc_abi"}
+rustc_ast = {path = "../rustc_ast"}
+rustc_ast_pretty = {path = "../rustc_ast_pretty"}
+rustc_data_structures = {path = "../rustc_data_structures"}
+rustc_macros = {path = "../rustc_macros"}
+rustc_serialize = {path = "../rustc_serialize"}
+rustc_span = {path = "../rustc_span"}
+thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index def6b16ee8ae..969bce7ae20a 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -1,9 +1,12 @@
 use rustc_abi::Align;
-use rustc_ast as ast;
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_ast::token::CommentKind;
+use rustc_ast::{self as ast, AttrStyle};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
+use rustc_span::hygiene::Transparency;
 use rustc_span::{Span, Symbol};
+use thin_vec::ThinVec;
 
-use crate::RustcVersion;
+use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability};
 
 #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 pub enum InlineAttr {
@@ -35,14 +38,26 @@ pub enum InstructionSetAttr {
     ArmT32,
 }
 
-#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)]
 pub enum OptimizeAttr {
-    None,
+    /// No `#[optimize(..)]` attribute
+    #[default]
+    Default,
+    /// `#[optimize(none)]`
+    DoNotOptimize,
+    /// `#[optimize(speed)]`
     Speed,
+    /// `#[optimize(size)]`
     Size,
 }
 
-#[derive(Clone, Debug, Encodable, Decodable)]
+impl OptimizeAttr {
+    pub fn do_not_optimize(&self) -> bool {
+        matches!(self, Self::DoNotOptimize)
+    }
+}
+
+#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)]
 pub enum DiagnosticAttribute {
     // tidy-alphabetical-start
     DoNotRecommend,
@@ -50,7 +65,7 @@ pub enum DiagnosticAttribute {
     // tidy-alphabetical-end
 }
 
-#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
+#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic, PrintAttribute)]
 pub enum ReprAttr {
     ReprInt(IntType),
     ReprRust,
@@ -59,6 +74,8 @@ pub enum ReprAttr {
     ReprSimd,
     ReprTransparent,
     ReprAlign(Align),
+    // this one is just so we can emit a lint for it
+    ReprEmpty,
 }
 pub use ReprAttr::*;
 
@@ -68,13 +85,13 @@ pub enum TransparencyError {
 }
 
 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
-#[derive(Encodable, Decodable)]
+#[derive(Encodable, Decodable, HashStable_Generic, PrintAttribute)]
 pub enum IntType {
     SignedInt(ast::IntTy),
     UnsignedInt(ast::UintTy),
 }
 
-#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
 pub struct Deprecation {
     pub since: DeprecatedSince,
     /// The note to issue a reason.
@@ -86,7 +103,7 @@ pub struct Deprecation {
 }
 
 /// Release in which an API is deprecated.
-#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)]
 pub enum DeprecatedSince {
     RustcVersion(RustcVersion),
     /// Deprecated in the future ("to be determined").
@@ -120,3 +137,65 @@ impl Deprecation {
         matches!(self.since, DeprecatedSince::RustcVersion(_))
     }
 }
+
+/// Represent parsed, *built in*, inert attributes.
+///
+/// That means attributes that are not actually ever expanded.
+/// For more information on this, see the module docs on the [`rustc_attr_parsing`] crate.
+/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler.
+/// These are kept around after the AST, into the HIR and further on.
+///
+/// The word "parsed" could be a little misleading here, because the parser already parses
+/// attributes early on. However, the result, an [`ast::Attribute`]
+/// is only parsed at a high level, still containing a token stream in many cases. That is
+/// because the structure of the contents varies from attribute to attribute.
+/// With a parsed attribute I mean that each attribute is processed individually into a
+/// final structure, which on-site (the place where the attribute is useful for, think the
+/// the place where `must_use` is checked) little to no extra parsing or validating needs to
+/// happen.
+///
+/// For more docs, look in [`rustc_attr_parsing`].
+///
+/// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
+#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
+pub enum AttributeKind {
+    // tidy-alphabetical-start
+    AllowConstFnUnstable(ThinVec<Symbol>),
+    AllowInternalUnstable(ThinVec<(Symbol, Span)>),
+    BodyStability {
+        stability: DefaultBodyStability,
+        /// Span of the `#[rustc_default_body_unstable(...)]` attribute
+        span: Span,
+    },
+    Confusables {
+        symbols: ThinVec<Symbol>,
+        // FIXME(jdonszelmann): remove when target validation code is moved
+        first_span: Span,
+    },
+    ConstStability {
+        stability: PartialConstStability,
+        /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
+        span: Span,
+    },
+    ConstStabilityIndirect,
+    Deprecation {
+        deprecation: Deprecation,
+        span: Span,
+    },
+    Diagnostic(DiagnosticAttribute),
+    DocComment {
+        style: AttrStyle,
+        kind: CommentKind,
+        span: Span,
+        comment: Symbol,
+    },
+    MacroTransparency(Transparency),
+    Repr(ThinVec<(ReprAttr, Span)>),
+    RustcMacroEdition2021,
+    Stability {
+        stability: Stability,
+        /// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute
+        span: Span,
+    },
+    // tidy-alphabetical-end
+}
diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs
index 4f204aeab640..c61b44b273de 100644
--- a/compiler/rustc_attr_data_structures/src/lib.rs
+++ b/compiler/rustc_attr_data_structures/src/lib.rs
@@ -3,14 +3,197 @@
 #![doc(rust_logo)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 mod attributes;
 mod stability;
 mod version;
 
+use std::num::NonZero;
+
 pub use attributes::*;
-pub(crate) use rustc_session::HashStableContext;
+use rustc_abi::Align;
+use rustc_ast::token::CommentKind;
+use rustc_ast::{AttrStyle, IntTy, UintTy};
+use rustc_ast_pretty::pp::Printer;
+use rustc_span::hygiene::Transparency;
+use rustc_span::{Span, Symbol};
 pub use stability::*;
+use thin_vec::ThinVec;
 pub use version::*;
+
+/// Requirements for a `StableHashingContext` to be used in this crate.
+/// This is a hack to allow using the `HashStable_Generic` derive macro
+/// instead of implementing everything in `rustc_middle`.
+pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {}
+
+/// This trait is used to print attributes in `rustc_hir_pretty`.
+///
+/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
+/// The output will look a lot like a `Debug` implementation, but fields of several types
+/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
+/// representation much.
+pub trait PrintAttribute {
+    /// Whether or not this will render as something meaningful, or if it's skipped
+    /// (which will force the containing struct to also skip printing a comma
+    /// and the field name).
+    fn should_render(&self) -> bool;
+
+    fn print_attribute(&self, p: &mut Printer);
+}
+
+impl<T: PrintAttribute> PrintAttribute for &T {
+    fn should_render(&self) -> bool {
+        T::should_render(self)
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        T::print_attribute(self, p)
+    }
+}
+impl<T: PrintAttribute> PrintAttribute for Option<T> {
+    fn should_render(&self) -> bool {
+        self.as_ref().is_some_and(|x| x.should_render())
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        if let Some(i) = self {
+            T::print_attribute(i, p)
+        }
+    }
+}
+impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
+    fn should_render(&self) -> bool {
+        self.is_empty() || self[0].should_render()
+    }
+
+    fn print_attribute(&self, p: &mut Printer) {
+        let mut last_printed = false;
+        p.word("[");
+        for i in self {
+            if last_printed {
+                p.word_space(",");
+            }
+            i.print_attribute(p);
+            last_printed = i.should_render();
+        }
+        p.word("]");
+    }
+}
+macro_rules! print_skip {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { false }
+            fn print_attribute(&self, _: &mut Printer) { }
+        })*
+    };
+}
+
+macro_rules! print_disp {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { true }
+            fn print_attribute(&self, p: &mut Printer) {
+                p.word(format!("{}", self));
+            }
+        }
+    )*};
+}
+macro_rules! print_debug {
+    ($($t: ty),* $(,)?) => {$(
+        impl PrintAttribute for $t {
+            fn should_render(&self) -> bool { true }
+            fn print_attribute(&self, p: &mut Printer) {
+                p.word(format!("{:?}", self));
+            }
+        }
+    )*};
+}
+
+macro_rules! print_tup {
+    (num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* };
+    () => {};
+    ($t: ident $($ts: ident)*) => {
+        #[allow(non_snake_case, unused)]
+        impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
+            fn should_render(&self) -> bool {
+                let ($t, $($ts),*) = self;
+                print_tup!(num_should_render $t $($ts)*) != 0
+            }
+
+            fn print_attribute(&self, p: &mut Printer) {
+                let ($t, $($ts),*) = self;
+                let parens = print_tup!(num_should_render $t $($ts)*) > 1;
+                if parens {
+                    p.popen();
+                }
+
+                let mut printed_anything = $t.should_render();
+
+                $t.print_attribute(p);
+
+                $(
+                    if $ts.should_render() {
+                        if printed_anything {
+                            p.word_space(",");
+                        }
+                        printed_anything = true;
+                    }
+                    $ts.print_attribute(p);
+                )*
+
+                if parens {
+                    p.pclose();
+                }
+            }
+        }
+
+        print_tup!($($ts)*);
+    };
+}
+
+print_tup!(A B C D E F G H);
+print_skip!(Span, ());
+print_disp!(u16, bool, NonZero<u32>);
+print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
+
+/// Finds attributes in sequences of attributes by pattern matching.
+///
+/// A little like `matches` but for attributes.
+///
+/// ```rust,ignore (illustrative)
+/// // finds the repr attribute
+/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
+///
+/// }
+///
+/// // checks if one has matched
+/// if find_attr!(attrs, AttributeKind::Repr(_)) {
+///
+/// }
+/// ```
+///
+/// Often this requires you to first end up with a list of attributes.
+/// A common way to get those is through `tcx.get_all_attrs(did)`
+#[macro_export]
+macro_rules! find_attr {
+    ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
+        $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
+    }};
+
+    ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
+        'done: {
+            for i in $attributes_list {
+                let i: &rustc_hir::Attribute = i;
+                match i {
+                    rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
+                        break 'done Some($e);
+                    }
+                    _ => {}
+                }
+            }
+
+            None
+        }
+    }};
+}
diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs
index dfda04387ec5..c0ca08a60f8b 100644
--- a/compiler/rustc_attr_data_structures/src/stability.rs
+++ b/compiler/rustc_attr_data_structures/src/stability.rs
@@ -1,9 +1,9 @@
 use std::num::NonZero;
 
-use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
 use rustc_span::{Symbol, sym};
 
-use crate::RustcVersion;
+use crate::{PrintAttribute, RustcVersion};
 
 /// The version placeholder that recently stabilized features contain inside the
 /// `since` field of the `#[stable]` attribute.
@@ -21,7 +21,7 @@ pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N");
 /// - `#[stable]`
 /// - `#[unstable]`
 #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub struct Stability {
     pub level: StabilityLevel,
     pub feature: Symbol,
@@ -43,7 +43,7 @@ impl Stability {
 
 /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
 #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub struct ConstStability {
     pub level: StabilityLevel,
     pub feature: Symbol,
@@ -83,7 +83,7 @@ impl ConstStability {
 /// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked`
 /// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes
 #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub struct PartialConstStability {
     pub level: StabilityLevel,
     pub feature: Symbol,
@@ -101,19 +101,9 @@ impl PartialConstStability {
     }
 }
 
-#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
-#[derive(HashStable_Generic)]
-pub enum AllowedThroughUnstableModules {
-    /// This does not get a deprecation warning. We still generally would prefer people to use the
-    /// fully stable path, and a warning will likely be emitted in the future.
-    WithoutDeprecation,
-    /// Emit the given deprecation warning.
-    WithDeprecation(Symbol),
-}
-
 /// The available stability levels.
 #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub enum StabilityLevel {
     /// `#[unstable]`
     Unstable {
@@ -147,14 +137,15 @@ pub enum StabilityLevel {
     Stable {
         /// Rust release which stabilized this feature.
         since: StableSince,
-        /// This is `Some` if this item allowed to be referred to on stable via unstable modules.
-        allowed_through_unstable_modules: Option<AllowedThroughUnstableModules>,
+        /// This is `Some` if this item allowed to be referred to on stable via unstable modules;
+        /// the `Symbol` is the deprecation message printed in that case.
+        allowed_through_unstable_modules: Option<Symbol>,
     },
 }
 
 /// Rust release in which a feature is stabilized.
 #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub enum StableSince {
     /// also stores the original symbol for printing
     Version(RustcVersion),
@@ -180,7 +171,7 @@ impl StabilityLevel {
 }
 
 #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub enum UnstableReason {
     None,
     Default,
@@ -189,7 +180,7 @@ pub enum UnstableReason {
 
 /// Represents the `#[rustc_default_body_unstable]` attribute.
 #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub struct DefaultBodyStability {
     pub level: StabilityLevel,
     pub feature: Symbol,
diff --git a/compiler/rustc_attr_data_structures/src/version.rs b/compiler/rustc_attr_data_structures/src/version.rs
index 6be875ad4be8..69b0e041d819 100644
--- a/compiler/rustc_attr_data_structures/src/version.rs
+++ b/compiler/rustc_attr_data_structures/src/version.rs
@@ -1,9 +1,13 @@
 use std::fmt::{self, Display};
 
-use rustc_macros::{Decodable, Encodable, HashStable_Generic, current_rustc_version};
+use rustc_macros::{
+    Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
+};
+
+use crate::PrintAttribute;
 
 #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[derive(HashStable_Generic)]
+#[derive(HashStable_Generic, PrintAttribute)]
 pub struct RustcVersion {
     pub major: u16,
     pub minor: u16,
diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml
index 7ccedf40c3fa..32029137268b 100644
--- a/compiler/rustc_attr_parsing/Cargo.toml
+++ b/compiler/rustc_attr_parsing/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustc_attr_parsing"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
@@ -9,13 +9,13 @@ rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
-rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
+rustc_hir = { path = "../rustc_hir" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_macros = { path = "../rustc_macros" }
-rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
+thin-vec = "0.2.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index faa2865cb913..45174c9582d3 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -6,6 +6,8 @@ attr_parsing_deprecated_item_suggestion =
     .help = add `#![feature(deprecated_suggestion)]` to the crate root
     .note = see #94785 for more details
 
+attr_parsing_empty_confusables =
+    expected at least one confusable name
 attr_parsing_expected_one_cfg_pattern =
     expected 1 cfg-pattern
 
@@ -21,8 +23,8 @@ attr_parsing_expects_feature_list =
 attr_parsing_expects_features =
     `{$name}` expects feature names
 
-attr_parsing_incorrect_meta_item =
-    incorrect meta item
+attr_parsing_incorrect_meta_item = expected a quoted string literal
+attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
 
 attr_parsing_incorrect_repr_format_align_one_arg =
     incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@@ -88,18 +90,20 @@ attr_parsing_multiple_stability_levels =
 attr_parsing_non_ident_feature =
     'feature' is not an identifier
 
+attr_parsing_repr_ident =
+    meta item in `repr` must be an identifier
+
 attr_parsing_rustc_allowed_unstable_pairing =
     `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
 
-attr_parsing_rustc_const_stable_indirect_pairing =
-    `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied
-
 attr_parsing_rustc_promotable_pairing =
     `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
 
 attr_parsing_soft_no_args =
     `soft` should not have any arguments
 
+attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library
+
 attr_parsing_unknown_meta_item =
     unknown meta item '{$item}'
     .label = expected one of {$expected}
@@ -107,6 +111,10 @@ attr_parsing_unknown_meta_item =
 attr_parsing_unknown_version_literal =
     unknown version literal format, assuming it refers to a future version
 
+attr_parsing_unrecognized_repr_hint =
+    unrecognized representation hint
+    .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+
 attr_parsing_unstable_cfg_target_compact =
     compact `cfg(target(..))` is experimental and subject to change
 
@@ -122,3 +130,8 @@ attr_parsing_unsupported_literal_generic =
     unsupported literal
 attr_parsing_unsupported_literal_suggestion =
     consider removing the prefix
+
+attr_parsing_unused_multiple =
+    multiple `{$name}` attributes
+    .suggestion = remove this attribute
+    .note = attribute also specified here
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
index 471168ed4f56..d37ede86cfd2 100644
--- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -1,49 +1,67 @@
-use rustc_ast::attr::{AttributeExt, filter_by_name};
-use rustc_session::Session;
-use rustc_span::{Symbol, sym};
+use std::iter;
 
+use rustc_attr_data_structures::AttributeKind;
+use rustc_span::{Span, Symbol, sym};
+
+use super::{CombineAttributeParser, ConvertFn};
+use crate::context::AcceptContext;
+use crate::parser::ArgParser;
 use crate::session_diagnostics;
 
-pub fn allow_internal_unstable<'a>(
-    sess: &'a Session,
-    attrs: &'a [impl AttributeExt],
-) -> impl Iterator<Item = Symbol> + 'a {
-    allow_unstable(sess, attrs, sym::allow_internal_unstable)
+pub(crate) struct AllowInternalUnstableParser;
+impl CombineAttributeParser for AllowInternalUnstableParser {
+    const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable];
+    type Item = (Symbol, Span);
+    const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
+
+    fn extend<'a>(
+        cx: &'a AcceptContext<'a>,
+        args: &'a ArgParser<'a>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'a {
+        parse_unstable(cx, args, Self::PATH[0]).into_iter().zip(iter::repeat(cx.attr_span))
+    }
 }
 
-pub fn rustc_allow_const_fn_unstable<'a>(
-    sess: &'a Session,
-    attrs: &'a [impl AttributeExt],
-) -> impl Iterator<Item = Symbol> + 'a {
-    allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
+pub(crate) struct AllowConstFnUnstableParser;
+impl CombineAttributeParser for AllowConstFnUnstableParser {
+    const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable];
+    type Item = Symbol;
+    const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
+
+    fn extend<'a>(
+        cx: &'a AcceptContext<'a>,
+        args: &'a ArgParser<'a>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'a {
+        parse_unstable(cx, args, Self::PATH[0])
+    }
 }
 
-fn allow_unstable<'a>(
-    sess: &'a Session,
-    attrs: &'a [impl AttributeExt],
+fn parse_unstable<'a>(
+    cx: &AcceptContext<'_>,
+    args: &'a ArgParser<'a>,
     symbol: Symbol,
-) -> impl Iterator<Item = Symbol> + 'a {
-    let attrs = filter_by_name(attrs, symbol);
-    let list = attrs
-        .filter_map(move |attr| {
-            attr.meta_item_list().or_else(|| {
-                sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList {
-                    span: attr.span(),
-                    name: symbol.to_ident_string(),
-                });
-                None
-            })
-        })
-        .flatten();
-
-    list.into_iter().filter_map(move |it| {
-        let name = it.ident().map(|ident| ident.name);
-        if name.is_none() {
-            sess.dcx().emit_err(session_diagnostics::ExpectsFeatures {
-                span: it.span(),
+) -> impl IntoIterator<Item = Symbol> {
+    let mut res = Vec::new();
+
+    let Some(list) = args.list() else {
+        cx.emit_err(session_diagnostics::ExpectsFeatureList {
+            span: cx.attr_span,
+            name: symbol.to_ident_string(),
+        });
+        return res;
+    };
+
+    for param in list.mixed() {
+        let param_span = param.span();
+        if let Some(ident) = param.meta_item().and_then(|i| i.word_without_args()) {
+            res.push(ident.name);
+        } else {
+            cx.emit_err(session_diagnostics::ExpectsFeatures {
+                span: param_span,
                 name: symbol.to_ident_string(),
             });
         }
-        name
-    })
+    }
+
+    res
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
index bb9aaaa2fea9..48297b2ebd8c 100644
--- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
@@ -1,6 +1,4 @@
-//! Parsing and validation of builtin attributes
-
-use rustc_ast::{self as ast, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
+use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
 use rustc_ast_pretty::pprust;
 use rustc_attr_data_structures::RustcVersion;
 use rustc_feature::{Features, GatedCfg, find_gated_cfg};
@@ -9,10 +7,10 @@ use rustc_session::config::ExpectedValues;
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::UNEXPECTED_CFGS;
 use rustc_session::parse::feature_err;
-use rustc_span::{Span, Symbol, kw, sym};
+use rustc_span::{Span, Symbol, sym};
 
-use crate::util::UnsupportedLiteralReason;
-use crate::{fluent_generated, parse_version, session_diagnostics};
+use crate::session_diagnostics::{self, UnsupportedLiteralReason};
+use crate::{fluent_generated, parse_version};
 
 #[derive(Clone, Debug)]
 pub struct Condition {
@@ -25,7 +23,7 @@ pub struct Condition {
 
 /// Tests if a cfg-pattern matches the cfg set
 pub fn cfg_matches(
-    cfg: &ast::MetaItemInner,
+    cfg: &MetaItemInner,
     sess: &Session,
     lint_node_id: NodeId,
     features: Option<&Features>,
@@ -80,7 +78,7 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Fea
 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
 /// evaluate individual items.
 pub fn eval_condition(
-    cfg: &ast::MetaItemInner,
+    cfg: &MetaItemInner,
     sess: &Session,
     features: Option<&Features>,
     eval: &mut impl FnMut(Condition) -> bool,
@@ -88,22 +86,8 @@ pub fn eval_condition(
     let dcx = sess.dcx();
 
     let cfg = match cfg {
-        ast::MetaItemInner::MetaItem(meta_item) => meta_item,
-        ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
-            if let Some(features) = features {
-                // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true`
-                // and `true`, and we want to keep the former working without feature gate
-                gate_cfg(
-                    &(
-                        if *b { kw::True } else { kw::False },
-                        sym::cfg_boolean_literals,
-                        |features: &Features| features.cfg_boolean_literals(),
-                    ),
-                    cfg.span(),
-                    sess,
-                    features,
-                );
-            }
+        MetaItemInner::MetaItem(meta_item) => meta_item,
+        MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
             return *b;
         }
         _ => {
@@ -118,7 +102,7 @@ pub fn eval_condition(
     };
 
     match &cfg.kind {
-        ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
+        MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
             try_gate_cfg(sym::version, cfg.span, sess, features);
             let (min_version, span) = match &mis[..] {
                 [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
@@ -150,7 +134,7 @@ pub fn eval_condition(
                 RustcVersion::CURRENT >= min_version
             }
         }
-        ast::MetaItemKind::List(mis) => {
+        MetaItemKind::List(mis) => {
             for mi in mis.iter() {
                 if mi.meta_item_or_bool().is_none() {
                     dcx.emit_err(session_diagnostics::UnsupportedLiteral {
@@ -209,12 +193,7 @@ pub fn eval_condition(
                             seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
                         }
 
-                        res & eval_condition(
-                            &ast::MetaItemInner::MetaItem(mi),
-                            sess,
-                            features,
-                            eval,
-                        )
+                        res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
                     })
                 }
                 _ => {
@@ -226,7 +205,7 @@ pub fn eval_condition(
                 }
             }
         }
-        ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
+        MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
             dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
             true
         }
@@ -239,7 +218,7 @@ pub fn eval_condition(
             });
             true
         }
-        ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
+        MetaItemKind::Word | MetaItemKind::NameValue(..) => {
             let ident = cfg.ident().expect("multi-segment cfg predicate");
             eval(Condition {
                 name: ident.name,
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
index 2ced759fd888..6cff952fcf22 100644
--- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -1,21 +1,58 @@
-//! Parsing and validation of builtin attributes
+use rustc_attr_data_structures::AttributeKind;
+use rustc_span::{Span, Symbol, sym};
+use thin_vec::ThinVec;
 
-use rustc_ast::MetaItemInner;
-use rustc_ast::attr::AttributeExt;
-use rustc_span::Symbol;
+use super::{AcceptMapping, AttributeParser};
+use crate::context::FinalizeContext;
+use crate::session_diagnostics;
 
-/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
-pub fn parse_confusables(attr: &impl AttributeExt) -> Option<Vec<Symbol>> {
-    let metas = attr.meta_item_list()?;
+#[derive(Default)]
+pub(crate) struct ConfusablesParser {
+    confusables: ThinVec<Symbol>,
+    first_span: Option<Span>,
+}
+
+impl AttributeParser for ConfusablesParser {
+    const ATTRIBUTES: AcceptMapping<Self> = &[(&[sym::rustc_confusables], |this, cx, args| {
+        let Some(list) = args.list() else {
+            // FIXME(jdonszelmann): error when not a list? Bring validation code here.
+            //       NOTE: currently subsequent attributes are silently ignored using
+            //       tcx.get_attr().
+            return;
+        };
+
+        if list.is_empty() {
+            cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
+        }
+
+        for param in list.mixed() {
+            let span = param.span();
 
-    let mut candidates = Vec::new();
+            let Some(lit) = param.lit() else {
+                cx.emit_err(session_diagnostics::IncorrectMetaItem {
+                    span,
+                    suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
+                        lo: span.shrink_to_lo(),
+                        hi: span.shrink_to_hi(),
+                    }),
+                });
+                continue;
+            };
 
-    for meta in metas {
-        let MetaItemInner::Lit(meta_lit) = meta else {
+            this.confusables.push(lit.symbol);
+        }
+
+        this.first_span.get_or_insert(cx.attr_span);
+    })];
+
+    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+        if self.confusables.is_empty() {
             return None;
-        };
-        candidates.push(meta_lit.symbol);
-    }
+        }
 
-    Some(candidates)
+        Some(AttributeKind::Confusables {
+            symbols: self.confusables,
+            first_span: self.first_span.unwrap(),
+        })
+    }
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
index d7415a7198fd..7d1417446b21 100644
--- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -1,121 +1,122 @@
-//! Parsing and validation of builtin attributes
-
-use rustc_ast::attr::AttributeExt;
-use rustc_ast::{MetaItem, MetaItemInner};
-use rustc_ast_pretty::pprust;
-use rustc_attr_data_structures::{DeprecatedSince, Deprecation};
-use rustc_feature::Features;
-use rustc_session::Session;
+use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation};
+use rustc_span::symbol::Ident;
 use rustc_span::{Span, Symbol, sym};
 
-use super::util::UnsupportedLiteralReason;
-use crate::{parse_version, session_diagnostics};
+use super::SingleAttributeParser;
+use super::util::parse_version;
+use crate::context::AcceptContext;
+use crate::parser::ArgParser;
+use crate::session_diagnostics;
+use crate::session_diagnostics::UnsupportedLiteralReason;
 
-/// Finds the deprecation attribute. `None` if none exists.
-pub fn find_deprecation(
-    sess: &Session,
-    features: &Features,
-    attrs: &[impl AttributeExt],
-) -> Option<(Deprecation, Span)> {
-    let mut depr: Option<(Deprecation, Span)> = None;
-    let is_rustc = features.staged_api();
+pub(crate) struct DeprecationParser;
 
-    'outer: for attr in attrs {
-        if !attr.has_name(sym::deprecated) {
-            continue;
+fn get(
+    cx: &AcceptContext<'_>,
+    ident: Ident,
+    param_span: Span,
+    arg: &ArgParser<'_>,
+    item: &Option<Symbol>,
+) -> Option<Symbol> {
+    if item.is_some() {
+        cx.emit_err(session_diagnostics::MultipleItem {
+            span: param_span,
+            item: ident.to_string(),
+        });
+        return None;
+    }
+    if let Some(v) = arg.name_value() {
+        if let Some(value_str) = v.value_as_str() {
+            Some(value_str)
+        } else {
+            let lit = v.value_as_lit();
+            cx.emit_err(session_diagnostics::UnsupportedLiteral {
+                span: v.value_span,
+                reason: UnsupportedLiteralReason::DeprecatedString,
+                is_bytestr: lit.kind.is_bytestr(),
+                start_point_span: cx.sess().source_map().start_point(lit.span),
+            });
+            None
         }
+    } else {
+        // FIXME(jdonszelmann): suggestion?
+        cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
+        None
+    }
+}
+
+impl SingleAttributeParser for DeprecationParser {
+    const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated];
+
+    fn on_duplicate(cx: &AcceptContext<'_>, first_span: rustc_span::Span) {
+        // FIXME(jdonszelmann): merge with errors from check_attrs.rs
+        cx.emit_err(session_diagnostics::UnusedMultiple {
+            this: cx.attr_span,
+            other: first_span,
+            name: sym::deprecated,
+        });
+    }
+
+    fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let features = cx.features();
 
         let mut since = None;
         let mut note = None;
         let mut suggestion = None;
 
-        if attr.is_doc_comment() {
-            continue;
-        } else if attr.is_word() {
-        } else if let Some(value) = attr.value_str() {
-            note = Some(value)
-        } else if let Some(list) = attr.meta_item_list() {
-            let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
-                if item.is_some() {
-                    sess.dcx().emit_err(session_diagnostics::MultipleItem {
-                        span: meta.span,
-                        item: pprust::path_to_string(&meta.path),
+        let is_rustc = features.staged_api();
+
+        if let Some(value) = args.name_value()
+            && let Some(value_str) = value.value_as_str()
+        {
+            note = Some(value_str)
+        } else if let Some(list) = args.list() {
+            for param in list.mixed() {
+                let param_span = param.span();
+                let Some(param) = param.meta_item() else {
+                    cx.emit_err(session_diagnostics::UnsupportedLiteral {
+                        span: param_span,
+                        reason: UnsupportedLiteralReason::DeprecatedKvPair,
+                        is_bytestr: false,
+                        start_point_span: cx.sess().source_map().start_point(param_span),
                     });
-                    return false;
-                }
-                if let Some(v) = meta.value_str() {
-                    *item = Some(v);
-                    true
-                } else {
-                    if let Some(lit) = meta.name_value_literal() {
-                        sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                            span: lit.span,
-                            reason: UnsupportedLiteralReason::DeprecatedString,
-                            is_bytestr: lit.kind.is_bytestr(),
-                            start_point_span: sess.source_map().start_point(lit.span),
-                        });
-                    } else {
-                        sess.dcx()
-                            .emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
-                    }
-                    false
-                }
-            };
+                    return None;
+                };
 
-            for meta in &list {
-                match meta {
-                    MetaItemInner::MetaItem(mi) => match mi.name_or_empty() {
-                        sym::since => {
-                            if !get(mi, &mut since) {
-                                continue 'outer;
-                            }
-                        }
-                        sym::note => {
-                            if !get(mi, &mut note) {
-                                continue 'outer;
-                            }
-                        }
-                        sym::suggestion => {
-                            if !features.deprecated_suggestion() {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::DeprecatedItemSuggestion {
-                                        span: mi.span,
-                                        is_nightly: sess.is_nightly_build(),
-                                        details: (),
-                                    },
-                                );
-                            }
+                let (ident, arg) = param.word_or_empty();
 
-                            if !get(mi, &mut suggestion) {
-                                continue 'outer;
-                            }
-                        }
-                        _ => {
-                            sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
-                                span: meta.span(),
-                                item: pprust::path_to_string(&mi.path),
-                                expected: if features.deprecated_suggestion() {
-                                    &["since", "note", "suggestion"]
-                                } else {
-                                    &["since", "note"]
-                                },
+                match ident.name {
+                    sym::since => {
+                        since = Some(get(cx, ident, param_span, arg, &since)?);
+                    }
+                    sym::note => {
+                        note = Some(get(cx, ident, param_span, arg, &note)?);
+                    }
+                    sym::suggestion => {
+                        if !features.deprecated_suggestion() {
+                            cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
+                                span: param_span,
+                                is_nightly: cx.sess().is_nightly_build(),
+                                details: (),
                             });
-                            continue 'outer;
                         }
-                    },
-                    MetaItemInner::Lit(lit) => {
-                        sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                            span: lit.span,
-                            reason: UnsupportedLiteralReason::DeprecatedKvPair,
-                            is_bytestr: false,
-                            start_point_span: sess.source_map().start_point(lit.span),
+
+                        suggestion = Some(get(cx, ident, param_span, arg, &suggestion)?);
+                    }
+                    _ => {
+                        cx.emit_err(session_diagnostics::UnknownMetaItem {
+                            span: param_span,
+                            item: ident.to_string(),
+                            expected: if features.deprecated_suggestion() {
+                                &["since", "note", "suggestion"]
+                            } else {
+                                &["since", "note"]
+                            },
                         });
-                        continue 'outer;
+                        return None;
                     }
                 }
             }
-        } else {
-            continue;
         }
 
         let since = if let Some(since) = since {
@@ -126,23 +127,24 @@ pub fn find_deprecation(
             } else if let Some(version) = parse_version(since) {
                 DeprecatedSince::RustcVersion(version)
             } else {
-                sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
+                cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
                 DeprecatedSince::Err
             }
         } else if is_rustc {
-            sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
+            cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
             DeprecatedSince::Err
         } else {
             DeprecatedSince::Unspecified
         };
 
         if is_rustc && note.is_none() {
-            sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span() });
-            continue;
+            cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
+            return None;
         }
 
-        depr = Some((Deprecation { since, note, suggestion }, attr.span()));
+        Some(AttributeKind::Deprecation {
+            deprecation: Deprecation { since, note, suggestion },
+            span: cx.attr_span,
+        })
     }
-
-    depr
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index a78e0b54b64a..bac111159db5 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -1,17 +1,153 @@
-mod allow_unstable;
-mod cfg;
-mod confusables;
-mod deprecation;
-mod repr;
-mod stability;
-mod transparency;
-
-pub mod util;
-
-pub use allow_unstable::*;
-pub use cfg::*;
-pub use confusables::*;
-pub use deprecation::*;
-pub use repr::*;
-pub use stability::*;
-pub use transparency::*;
+//! This module defines traits for attribute parsers, little state machines that recognize and parse
+//! attributes out of a longer list of attributes. The main trait is called [`AttributeParser`].
+//! You can find more docs about [`AttributeParser`]s on the trait itself.
+//! However, for many types of attributes, implementing [`AttributeParser`] is not necessary.
+//! It allows for a lot of flexibility you might not want.
+//!
+//! Specifically, you might not care about managing the state of your [`AttributeParser`]
+//! state machine yourself. In this case you can choose to implement:
+//!
+//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
+//! appears more than once in a list of attributes
+//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
+//! contents of attributes, if an attribute appear multiple times in a list
+//!
+//! Attributes should be added to [`ATTRIBUTE_MAPPING`](crate::context::ATTRIBUTE_MAPPING) to be parsed.
+
+use std::marker::PhantomData;
+
+use rustc_attr_data_structures::AttributeKind;
+use rustc_span::Span;
+use thin_vec::ThinVec;
+
+use crate::context::{AcceptContext, FinalizeContext};
+use crate::parser::ArgParser;
+
+pub(crate) mod allow_unstable;
+pub(crate) mod cfg;
+pub(crate) mod confusables;
+pub(crate) mod deprecation;
+pub(crate) mod repr;
+pub(crate) mod rustc;
+pub(crate) mod stability;
+pub(crate) mod transparency;
+pub(crate) mod util;
+
+type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
+type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];
+
+/// An [`AttributeParser`] is a type which searches for syntactic attributes.
+///
+/// Parsers are often tiny state machines that gets to see all syntactical attributes on an item.
+/// [`Default::default`] creates a fresh instance that sits in some kind of initial state, usually that the
+/// attribute it is looking for was not yet seen.
+///
+/// Then, it defines what paths this group will accept in [`AttributeParser::ATTRIBUTES`].
+/// These are listed as pairs, of symbols and function pointers. The function pointer will
+/// be called when that attribute is found on an item, which can influence the state of the little
+/// state machine.
+///
+/// Finally, after all attributes on an item have been seen, and possibly been accepted,
+/// the [`finalize`](AttributeParser::finalize) functions for all attribute parsers are called. Each can then report
+/// whether it has seen the attribute it has been looking for.
+///
+/// The state machine is automatically reset to parse attributes on the next item.
+pub(crate) trait AttributeParser: Default + 'static {
+    /// The symbols for the attributes that this parser is interested in.
+    ///
+    /// If an attribute has this symbol, the `accept` function will be called on it.
+    const ATTRIBUTES: AcceptMapping<Self>;
+
+    /// The parser has gotten a chance to accept the attributes on an item,
+    /// here it can produce an attribute.
+    fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>;
+}
+
+/// Alternative to [`AttributeParser`] that automatically handles state management.
+/// A slightly simpler and more restricted way to convert attributes.
+/// Assumes that an attribute can only appear a single time on an item,
+/// and errors when it sees more.
+///
+/// [`Single<T> where T: SingleAttributeParser`](Single) implements [`AttributeParser`].
+///
+/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
+/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
+pub(crate) trait SingleAttributeParser: 'static {
+    const PATH: &'static [rustc_span::Symbol];
+
+    /// Caled when a duplicate attribute is found.
+    ///
+    /// `first_span` is the span of the first occurrence of this attribute.
+    // FIXME(jdonszelmann): default error
+    fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span);
+
+    /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
+    fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
+}
+
+pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>);
+
+impl<T: SingleAttributeParser> Default for Single<T> {
+    fn default() -> Self {
+        Self(Default::default(), Default::default())
+    }
+}
+
+impl<T: SingleAttributeParser> AttributeParser for Single<T> {
+    const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
+        if let Some((_, s)) = group.1 {
+            T::on_duplicate(cx, s);
+            return;
+        }
+
+        if let Some(pa) = T::convert(cx, args) {
+            group.1 = Some((pa, cx.attr_span));
+        }
+    })];
+
+    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+        Some(self.1?.0)
+    }
+}
+
+type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
+
+/// Alternative to [`AttributeParser`] that automatically handles state management.
+/// If multiple attributes appear on an element, combines the values of each into a
+/// [`ThinVec`].
+/// [`Combine<T> where T: CombineAttributeParser`](Combine) implements [`AttributeParser`].
+///
+/// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
+/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
+pub(crate) trait CombineAttributeParser: 'static {
+    const PATH: &'static [rustc_span::Symbol];
+
+    type Item;
+    const CONVERT: ConvertFn<Self::Item>;
+
+    /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
+    fn extend<'a>(
+        cx: &'a AcceptContext<'a>,
+        args: &'a ArgParser<'a>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'a;
+}
+
+pub(crate) struct Combine<T: CombineAttributeParser>(
+    PhantomData<T>,
+    ThinVec<<T as CombineAttributeParser>::Item>,
+);
+
+impl<T: CombineAttributeParser> Default for Combine<T> {
+    fn default() -> Self {
+        Self(Default::default(), Default::default())
+    }
+}
+
+impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
+    const ATTRIBUTES: AcceptMapping<Self> =
+        &[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))];
+
+    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+        if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index 28c381160b87..26ca637faec6 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -1,15 +1,13 @@
-//! Parsing and validation of builtin attributes
-
 use rustc_abi::Align;
-use rustc_ast::attr::AttributeExt;
-use rustc_ast::{self as ast, MetaItemKind};
-use rustc_attr_data_structures::IntType;
-use rustc_attr_data_structures::ReprAttr::*;
-use rustc_session::Session;
-use rustc_span::{Symbol, sym};
+use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
+use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
+use rustc_span::{Span, Symbol, sym};
 
-use crate::ReprAttr;
-use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
+use super::{CombineAttributeParser, ConvertFn};
+use crate::context::AcceptContext;
+use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
+use crate::session_diagnostics;
+use crate::session_diagnostics::IncorrectReprFormatGenericCause;
 
 /// Parse #[repr(...)] forms.
 ///
@@ -18,185 +16,216 @@ use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
 /// the same discriminant size that the corresponding C enum would or C
 /// structure layout, `packed` to remove padding, and `transparent` to delegate representation
 /// concerns to the only non-ZST field.
-pub fn find_repr_attrs(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
-    if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
-}
+// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct?
+pub(crate) struct ReprParser;
 
-pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr> {
-    assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
-    let mut acc = Vec::new();
-    let dcx = sess.dcx();
-
-    if let Some(items) = attr.meta_item_list() {
-        for item in items {
-            let mut recognised = false;
-            if item.is_word() {
-                let hint = match item.name_or_empty() {
-                    sym::Rust => Some(ReprRust),
-                    sym::C => Some(ReprC),
-                    sym::packed => Some(ReprPacked(Align::ONE)),
-                    sym::simd => Some(ReprSimd),
-                    sym::transparent => Some(ReprTransparent),
-                    sym::align => {
-                        sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg {
-                            span: item.span(),
-                        });
-                        recognised = true;
-                        None
-                    }
-                    name => int_type_of_word(name).map(ReprInt),
-                };
-
-                if let Some(h) = hint {
-                    recognised = true;
-                    acc.push(h);
-                }
-            } else if let Some((name, value)) = item.singleton_lit_list() {
-                let mut literal_error = None;
-                let mut err_span = item.span();
-                if name == sym::align {
-                    recognised = true;
-                    match parse_alignment(&value.kind) {
-                        Ok(literal) => acc.push(ReprAlign(literal)),
-                        Err(message) => {
-                            err_span = value.span;
-                            literal_error = Some(message)
-                        }
-                    };
-                } else if name == sym::packed {
-                    recognised = true;
-                    match parse_alignment(&value.kind) {
-                        Ok(literal) => acc.push(ReprPacked(literal)),
-                        Err(message) => {
-                            err_span = value.span;
-                            literal_error = Some(message)
-                        }
-                    };
-                } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
-                    || int_type_of_word(name).is_some()
-                {
-                    recognised = true;
-                    sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
-                        span: item.span(),
-                        name: name.to_ident_string(),
-                    });
-                }
-                if let Some(literal_error) = literal_error {
-                    sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric {
-                        span: err_span,
-                        repr_arg: name.to_ident_string(),
-                        error_part: literal_error,
-                    });
-                }
-            } else if let Some(meta_item) = item.meta_item() {
-                match &meta_item.kind {
-                    MetaItemKind::NameValue(value) => {
-                        if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
-                            let name = meta_item.name_or_empty().to_ident_string();
-                            recognised = true;
-                            sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric {
-                                span: item.span(),
-                                repr_arg: &name,
-                                cause: IncorrectReprFormatGenericCause::from_lit_kind(
-                                    item.span(),
-                                    &value.kind,
-                                    &name,
-                                ),
-                            });
-                        } else if matches!(
-                            meta_item.name_or_empty(),
-                            sym::Rust | sym::C | sym::simd | sym::transparent
-                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
-                        {
-                            recognised = true;
-                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue {
-                                span: meta_item.span,
-                                name: meta_item.name_or_empty().to_ident_string(),
-                            });
-                        }
-                    }
-                    MetaItemKind::List(nested_items) => {
-                        if meta_item.has_name(sym::align) {
-                            recognised = true;
-                            if let [nested_item] = nested_items.as_slice() {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatExpectInteger {
-                                        span: nested_item.span(),
-                                    },
-                                );
-                            } else {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatAlignOneArg {
-                                        span: meta_item.span,
-                                    },
-                                );
-                            }
-                        } else if meta_item.has_name(sym::packed) {
-                            recognised = true;
-                            if let [nested_item] = nested_items.as_slice() {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatPackedExpectInteger {
-                                        span: nested_item.span(),
-                                    },
-                                );
-                            } else {
-                                sess.dcx().emit_err(
-                                    session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
-                                        span: meta_item.span,
-                                    },
-                                );
-                            }
-                        } else if matches!(
-                            meta_item.name_or_empty(),
-                            sym::Rust | sym::C | sym::simd | sym::transparent
-                        ) || int_type_of_word(meta_item.name_or_empty()).is_some()
-                        {
-                            recognised = true;
-                            sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen {
-                                span: meta_item.span,
-                                name: meta_item.name_or_empty().to_ident_string(),
-                            });
-                        }
-                    }
-                    _ => (),
-                }
-            }
-            if !recognised {
-                // Not a word we recognize. This will be caught and reported by
-                // the `check_mod_attrs` pass, but this pass doesn't always run
-                // (e.g. if we only pretty-print the source), so we have to gate
-                // the `span_delayed_bug` call as follows:
-                if sess.opts.pretty.is_none_or(|pp| pp.needs_analysis()) {
-                    dcx.span_delayed_bug(item.span(), "unrecognized representation hint");
-                }
+impl CombineAttributeParser for ReprParser {
+    type Item = (ReprAttr, Span);
+    const PATH: &'static [rustc_span::Symbol] = &[sym::repr];
+    const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
+
+    fn extend<'a>(
+        cx: &'a AcceptContext<'a>,
+        args: &'a ArgParser<'a>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'a {
+        let mut reprs = Vec::new();
+
+        let Some(list) = args.list() else {
+            return reprs;
+        };
+
+        if list.is_empty() {
+            // this is so validation can emit a lint
+            reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
+        }
+
+        for param in list.mixed() {
+            if let Some(_) = param.lit() {
+                cx.emit_err(session_diagnostics::ReprIdent { span: cx.attr_span });
+                continue;
             }
+
+            reprs.extend(
+                param.meta_item().and_then(|mi| parse_repr(cx, &mi)).map(|r| (r, param.span())),
+            );
         }
+
+        reprs
     }
-    acc
+}
+
+macro_rules! int_pat {
+    () => {
+        sym::i8
+            | sym::u8
+            | sym::i16
+            | sym::u16
+            | sym::i32
+            | sym::u32
+            | sym::i64
+            | sym::u64
+            | sym::i128
+            | sym::u128
+            | sym::isize
+            | sym::usize
+    };
 }
 
 fn int_type_of_word(s: Symbol) -> Option<IntType> {
-    use rustc_attr_data_structures::IntType::*;
+    use IntType::*;
 
     match s {
-        sym::i8 => Some(SignedInt(ast::IntTy::I8)),
-        sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
-        sym::i16 => Some(SignedInt(ast::IntTy::I16)),
-        sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
-        sym::i32 => Some(SignedInt(ast::IntTy::I32)),
-        sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
-        sym::i64 => Some(SignedInt(ast::IntTy::I64)),
-        sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
-        sym::i128 => Some(SignedInt(ast::IntTy::I128)),
-        sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
-        sym::isize => Some(SignedInt(ast::IntTy::Isize)),
-        sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
+        sym::i8 => Some(SignedInt(IntTy::I8)),
+        sym::u8 => Some(UnsignedInt(UintTy::U8)),
+        sym::i16 => Some(SignedInt(IntTy::I16)),
+        sym::u16 => Some(UnsignedInt(UintTy::U16)),
+        sym::i32 => Some(SignedInt(IntTy::I32)),
+        sym::u32 => Some(UnsignedInt(UintTy::U32)),
+        sym::i64 => Some(SignedInt(IntTy::I64)),
+        sym::u64 => Some(UnsignedInt(UintTy::U64)),
+        sym::i128 => Some(SignedInt(IntTy::I128)),
+        sym::u128 => Some(UnsignedInt(UintTy::U128)),
+        sym::isize => Some(SignedInt(IntTy::Isize)),
+        sym::usize => Some(UnsignedInt(UintTy::Usize)),
         _ => None,
     }
 }
 
-pub fn parse_alignment(node: &ast::LitKind) -> Result<Align, &'static str> {
-    if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
+fn parse_repr(cx: &AcceptContext<'_>, param: &MetaItemParser<'_>) -> Option<ReprAttr> {
+    use ReprAttr::*;
+
+    // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the
+    // structure.
+    let (ident, args) = param.word_or_empty();
+
+    match (ident.name, args) {
+        (sym::align, ArgParser::NoArgs) => {
+            cx.emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident.span });
+            None
+        }
+        (sym::align, ArgParser::List(l)) => parse_repr_align(cx, l, param.span(), AlignKind::Align),
+
+        (sym::packed, ArgParser::NoArgs) => Some(ReprPacked(Align::ONE)),
+        (sym::packed, ArgParser::List(l)) => {
+            parse_repr_align(cx, l, param.span(), AlignKind::Packed)
+        }
+
+        (sym::align | sym::packed, ArgParser::NameValue(l)) => {
+            cx.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
+                span: param.span(),
+                // FIXME(jdonszelmann) can just be a string in the diag type
+                repr_arg: &ident.to_string(),
+                cause: IncorrectReprFormatGenericCause::from_lit_kind(
+                    param.span(),
+                    &l.value_as_lit().kind,
+                    ident.name.as_str(),
+                ),
+            });
+            None
+        }
+
+        (sym::Rust, ArgParser::NoArgs) => Some(ReprRust),
+        (sym::C, ArgParser::NoArgs) => Some(ReprC),
+        (sym::simd, ArgParser::NoArgs) => Some(ReprSimd),
+        (sym::transparent, ArgParser::NoArgs) => Some(ReprTransparent),
+        (i @ int_pat!(), ArgParser::NoArgs) => {
+            // int_pat!() should make sure it always parses
+            Some(ReprInt(int_type_of_word(i).unwrap()))
+        }
+
+        (
+            sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(),
+            ArgParser::NameValue(_),
+        ) => {
+            cx.emit_err(session_diagnostics::InvalidReprHintNoValue {
+                span: param.span(),
+                name: ident.to_string(),
+            });
+            None
+        }
+        (sym::Rust | sym::C | sym::simd | sym::transparent | int_pat!(), ArgParser::List(_)) => {
+            cx.emit_err(session_diagnostics::InvalidReprHintNoParen {
+                span: param.span(),
+                name: ident.to_string(),
+            });
+            None
+        }
+
+        _ => {
+            cx.emit_err(session_diagnostics::UnrecognizedReprHint { span: param.span() });
+            None
+        }
+    }
+}
+
+enum AlignKind {
+    Packed,
+    Align,
+}
+
+fn parse_repr_align(
+    cx: &AcceptContext<'_>,
+    list: &MetaItemListParser<'_>,
+    param_span: Span,
+    align_kind: AlignKind,
+) -> Option<ReprAttr> {
+    use AlignKind::*;
+
+    let Some(align) = list.single() else {
+        match align_kind {
+            Packed => {
+                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
+                    span: param_span,
+                });
+            }
+            Align => {
+                cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
+                    span: param_span,
+                });
+            }
+        }
+
+        return None;
+    };
+
+    let Some(lit) = align.lit() else {
+        match align_kind {
+            Packed => {
+                cx.emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger {
+                    span: align.span(),
+                });
+            }
+            Align => {
+                cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
+                    span: align.span(),
+                });
+            }
+        }
+
+        return None;
+    };
+
+    match parse_alignment(&lit.kind) {
+        Ok(literal) => Some(match align_kind {
+            AlignKind::Packed => ReprAttr::ReprPacked(literal),
+            AlignKind::Align => ReprAttr::ReprAlign(literal),
+        }),
+        Err(message) => {
+            cx.emit_err(session_diagnostics::InvalidReprGeneric {
+                span: lit.span,
+                repr_arg: match align_kind {
+                    Packed => "packed".to_string(),
+                    Align => "align".to_string(),
+                },
+                error_part: message,
+            });
+            None
+        }
+    }
+}
+
+fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
+    if let LitKind::Int(literal, LitIntType::Unsuffixed) = node {
         // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first
         if literal.get().is_power_of_two() {
             // Only possible error is larger than 2^29
diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc.rs b/compiler/rustc_attr_parsing/src/attributes/rustc.rs
new file mode 100644
index 000000000000..bdd3bef2834b
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/rustc.rs
@@ -0,0 +1,19 @@
+use rustc_attr_data_structures::AttributeKind;
+use rustc_span::sym;
+
+use super::{AcceptContext, SingleAttributeParser};
+use crate::parser::ArgParser;
+
+pub(crate) struct RustcMacroEdition2021Parser;
+
+// FIXME(jdonszelmann): make these proper diagnostics
+impl SingleAttributeParser for RustcMacroEdition2021Parser {
+    const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_edition_2021];
+
+    fn on_duplicate(_cx: &crate::context::AcceptContext<'_>, _first_span: rustc_span::Span) {}
+
+    fn convert(_cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        assert!(args.no_args());
+        Some(AttributeKind::RustcMacroEdition2021)
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index bfbe51b27d88..bdad6b50186d 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -1,262 +1,254 @@
-//! Parsing and validation of builtin attributes
-
 use std::num::NonZero;
 
-use rustc_ast::MetaItem;
-use rustc_ast::attr::AttributeExt;
-use rustc_ast_pretty::pprust;
 use rustc_attr_data_structures::{
-    AllowedThroughUnstableModules, ConstStability, DefaultBodyStability, Stability, StabilityLevel,
+    AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel,
     StableSince, UnstableReason, VERSION_PLACEHOLDER,
 };
 use rustc_errors::ErrorGuaranteed;
-use rustc_session::Session;
 use rustc_span::{Span, Symbol, sym};
 
-use crate::attributes::util::UnsupportedLiteralReason;
-use crate::{parse_version, session_diagnostics};
-
-/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
-/// attributes in `attrs`. Returns `None` if no stability attributes are found.
-pub fn find_stability(
-    sess: &Session,
-    attrs: &[impl AttributeExt],
-    item_sp: Span,
-) -> Option<(Stability, Span)> {
-    let mut stab: Option<(Stability, Span)> = None;
-    let mut allowed_through_unstable_modules = None;
-
-    for attr in attrs {
-        match attr.name_or_empty() {
-            sym::rustc_allowed_through_unstable_modules => {
-                allowed_through_unstable_modules = Some(match attr.value_str() {
-                    Some(msg) => AllowedThroughUnstableModules::WithDeprecation(msg),
-                    None => AllowedThroughUnstableModules::WithoutDeprecation,
-                })
-            }
-            sym::unstable => {
-                if stab.is_some() {
-                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
-                        span: attr.span(),
-                    });
-                    break;
-                }
+use super::util::parse_version;
+use super::{AcceptMapping, AttributeParser, SingleAttributeParser};
+use crate::context::{AcceptContext, FinalizeContext};
+use crate::parser::{ArgParser, MetaItemParser};
+use crate::session_diagnostics::{self, UnsupportedLiteralReason};
+
+macro_rules! reject_outside_std {
+    ($cx: ident) => {
+        // Emit errors for non-staged-api crates.
+        if !$cx.features().staged_api() {
+            $cx.emit_err(session_diagnostics::StabilityOutsideStd { span: $cx.attr_span });
+            return;
+        }
+    };
+}
 
-                if let Some((feature, level)) = parse_unstability(sess, attr) {
-                    stab = Some((Stability { level, feature }, attr.span()));
-                }
-            }
-            sym::stable => {
-                if stab.is_some() {
-                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
-                        span: attr.span(),
-                    });
-                    break;
-                }
-                if let Some((feature, level)) = parse_stability(sess, attr) {
-                    stab = Some((Stability { level, feature }, attr.span()));
-                }
-            }
-            _ => {}
+#[derive(Default)]
+pub(crate) struct StabilityParser {
+    allowed_through_unstable_modules: Option<Symbol>,
+    stability: Option<(Stability, Span)>,
+}
+
+impl StabilityParser {
+    /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
+    fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
+        if let Some((_, _)) = self.stability {
+            cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
+            true
+        } else {
+            false
         }
     }
+}
 
-    if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules {
-        match &mut stab {
-            Some((
+impl AttributeParser for StabilityParser {
+    const ATTRIBUTES: AcceptMapping<Self> = &[
+        (&[sym::stable], |this, cx, args| {
+            reject_outside_std!(cx);
+            if !this.check_duplicate(cx)
+                && let Some((feature, level)) = parse_stability(cx, args)
+            {
+                this.stability = Some((Stability { level, feature }, cx.attr_span));
+            }
+        }),
+        (&[sym::unstable], |this, cx, args| {
+            reject_outside_std!(cx);
+            if !this.check_duplicate(cx)
+                && let Some((feature, level)) = parse_unstability(cx, args)
+            {
+                this.stability = Some((Stability { level, feature }, cx.attr_span));
+            }
+        }),
+        (&[sym::rustc_allowed_through_unstable_modules], |this, cx, args| {
+            reject_outside_std!(cx);
+            this.allowed_through_unstable_modules = args.name_value().and_then(|i| i.value_as_str())
+        }),
+    ];
+
+    fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+        if let Some(atum) = self.allowed_through_unstable_modules {
+            if let Some((
                 Stability {
-                    level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. },
+                    level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. },
                     ..
                 },
                 _,
-            )) => *in_stab = Some(allowed_through_unstable_modules),
-            _ => {
-                sess.dcx()
-                    .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
+            )) = self.stability
+            {
+                *allowed_through_unstable_modules = Some(atum);
+            } else {
+                cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing {
+                    span: cx.target_span,
+                });
             }
         }
-    }
 
-    stab
+        let (stability, span) = self.stability?;
+
+        Some(AttributeKind::Stability { stability, span })
+    }
 }
 
-/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
-/// attributes in `attrs`. Returns `None` if no stability attributes are found.
-pub fn find_const_stability(
-    sess: &Session,
-    attrs: &[impl AttributeExt],
-    item_sp: Span,
-) -> Option<(ConstStability, Span)> {
-    let mut const_stab: Option<(ConstStability, Span)> = None;
-    let mut promotable = false;
-    let mut const_stable_indirect = false;
-
-    for attr in attrs {
-        match attr.name_or_empty() {
-            sym::rustc_promotable => promotable = true,
-            sym::rustc_const_stable_indirect => const_stable_indirect = true,
-            sym::rustc_const_unstable => {
-                if const_stab.is_some() {
-                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
-                        span: attr.span(),
-                    });
-                    break;
-                }
+// FIXME(jdonszelmann) change to Single
+#[derive(Default)]
+pub(crate) struct BodyStabilityParser {
+    stability: Option<(DefaultBodyStability, Span)>,
+}
 
-                if let Some((feature, level)) = parse_unstability(sess, attr) {
-                    const_stab = Some((
-                        ConstStability {
-                            level,
-                            feature,
-                            const_stable_indirect: false,
-                            promotable: false,
-                        },
-                        attr.span(),
-                    ));
-                }
-            }
-            sym::rustc_const_stable => {
-                if const_stab.is_some() {
-                    sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels {
-                        span: attr.span(),
-                    });
-                    break;
-                }
-                if let Some((feature, level)) = parse_stability(sess, attr) {
-                    const_stab = Some((
-                        ConstStability {
-                            level,
-                            feature,
-                            const_stable_indirect: false,
-                            promotable: false,
-                        },
-                        attr.span(),
-                    ));
-                }
+impl AttributeParser for BodyStabilityParser {
+    const ATTRIBUTES: AcceptMapping<Self> =
+        &[(&[sym::rustc_default_body_unstable], |this, cx, args| {
+            reject_outside_std!(cx);
+            if this.stability.is_some() {
+                cx.dcx()
+                    .emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
+            } else if let Some((feature, level)) = parse_unstability(cx, args) {
+                this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span));
             }
-            _ => {}
-        }
-    }
+        })];
 
-    // Merge promotable and const_stable_indirect into stability info
-    if promotable {
-        match &mut const_stab {
-            Some((stab, _)) => stab.promotable = promotable,
-            _ => {
-                _ = sess
-                    .dcx()
-                    .emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp })
-            }
-        }
+    fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+        let (stability, span) = self.stability?;
+
+        Some(AttributeKind::BodyStability { stability, span })
     }
-    if const_stable_indirect {
-        match &mut const_stab {
-            Some((stab, _)) => {
-                if stab.is_const_unstable() {
-                    stab.const_stable_indirect = true;
-                } else {
-                    _ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing {
-                        span: item_sp,
-                    })
-                }
-            }
-            _ => {
-                // This function has no const stability attribute, but has `const_stable_indirect`.
-                // We ignore that; unmarked functions are subject to recursive const stability
-                // checks by default so we do carry out the user's intent.
-            }
-        }
+}
+
+pub(crate) struct ConstStabilityIndirectParser;
+// FIXME(jdonszelmann): single word attribute group when we have these
+impl SingleAttributeParser for ConstStabilityIndirectParser {
+    const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect];
+
+    // ignore
+    fn on_duplicate(_cx: &AcceptContext<'_>, _first_span: Span) {}
+
+    fn convert(_cx: &AcceptContext<'_>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::ConstStabilityIndirect)
     }
+}
 
-    const_stab
+#[derive(Default)]
+pub(crate) struct ConstStabilityParser {
+    promotable: bool,
+    stability: Option<(PartialConstStability, Span)>,
 }
 
-/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate
-/// without the `staged_api` feature.
-pub fn unmarked_crate_const_stab(
-    _sess: &Session,
-    attrs: &[impl AttributeExt],
-    regular_stab: Stability,
-) -> ConstStability {
-    assert!(regular_stab.level.is_unstable());
-    // The only attribute that matters here is `rustc_const_stable_indirect`.
-    // We enforce recursive const stability rules for those functions.
-    let const_stable_indirect =
-        attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect);
-    ConstStability {
-        feature: regular_stab.feature,
-        const_stable_indirect,
-        promotable: false,
-        level: regular_stab.level,
+impl ConstStabilityParser {
+    /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate.
+    fn check_duplicate(&self, cx: &AcceptContext<'_>) -> bool {
+        if let Some((_, _)) = self.stability {
+            cx.emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span });
+            true
+        } else {
+            false
+        }
     }
 }
 
-/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
-/// Returns `None` if no stability attributes are found.
-pub fn find_body_stability(
-    sess: &Session,
-    attrs: &[impl AttributeExt],
-) -> Option<(DefaultBodyStability, Span)> {
-    let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
-
-    for attr in attrs {
-        if attr.has_name(sym::rustc_default_body_unstable) {
-            if body_stab.is_some() {
-                sess.dcx()
-                    .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span() });
-                break;
+impl AttributeParser for ConstStabilityParser {
+    const ATTRIBUTES: AcceptMapping<Self> = &[
+        (&[sym::rustc_const_stable], |this, cx, args| {
+            reject_outside_std!(cx);
+
+            if !this.check_duplicate(cx)
+                && let Some((feature, level)) = parse_stability(cx, args)
+            {
+                this.stability = Some((
+                    PartialConstStability { level, feature, promotable: false },
+                    cx.attr_span,
+                ));
             }
-
-            if let Some((feature, level)) = parse_unstability(sess, attr) {
-                body_stab = Some((DefaultBodyStability { level, feature }, attr.span()));
+        }),
+        (&[sym::rustc_const_unstable], |this, cx, args| {
+            reject_outside_std!(cx);
+            if !this.check_duplicate(cx)
+                && let Some((feature, level)) = parse_unstability(cx, args)
+            {
+                this.stability = Some((
+                    PartialConstStability { level, feature, promotable: false },
+                    cx.attr_span,
+                ));
+            }
+        }),
+        (&[sym::rustc_promotable], |this, cx, _| {
+            reject_outside_std!(cx);
+            this.promotable = true;
+        }),
+    ];
+
+    fn finalize(mut self, cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
+        if self.promotable {
+            if let Some((ref mut stab, _)) = self.stability {
+                stab.promotable = true;
+            } else {
+                cx.dcx()
+                    .emit_err(session_diagnostics::RustcPromotablePairing { span: cx.target_span });
             }
         }
-    }
 
-    body_stab
+        let (stability, span) = self.stability?;
+
+        Some(AttributeKind::ConstStability { stability, span })
+    }
 }
 
-fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
+/// Tries to insert the value of a `key = value` meta item into an option.
+///
+/// Emits an error when either the option was already Some, or the arguments weren't of form
+/// `name = value`
+fn insert_value_into_option_or_error(
+    cx: &AcceptContext<'_>,
+    param: &MetaItemParser<'_>,
+    item: &mut Option<Symbol>,
+) -> Option<()> {
     if item.is_some() {
-        sess.dcx().emit_err(session_diagnostics::MultipleItem {
-            span: meta.span,
-            item: pprust::path_to_string(&meta.path),
+        cx.emit_err(session_diagnostics::MultipleItem {
+            span: param.span(),
+            item: param.path_without_args().to_string(),
         });
         None
-    } else if let Some(v) = meta.value_str() {
-        *item = Some(v);
+    } else if let Some(v) = param.args().name_value()
+        && let Some(s) = v.value_as_str()
+    {
+        *item = Some(s);
         Some(())
     } else {
-        sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
+        cx.emit_err(session_diagnostics::IncorrectMetaItem {
+            span: param.span(),
+            suggestion: None,
+        });
         None
     }
 }
 
 /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
 /// its stability information.
-fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
-    let metas = attr.meta_item_list()?;
-
+pub(crate) fn parse_stability(
+    cx: &AcceptContext<'_>,
+    args: &ArgParser<'_>,
+) -> Option<(Symbol, StabilityLevel)> {
     let mut feature = None;
     let mut since = None;
-    for meta in metas {
-        let Some(mi) = meta.meta_item() else {
-            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                span: meta.span(),
+
+    for param in args.list()?.mixed() {
+        let param_span = param.span();
+        let Some(param) = param.meta_item() else {
+            cx.emit_err(session_diagnostics::UnsupportedLiteral {
+                span: param_span,
                 reason: UnsupportedLiteralReason::Generic,
                 is_bytestr: false,
-                start_point_span: sess.source_map().start_point(meta.span()),
+                start_point_span: cx.sess().source_map().start_point(param_span),
             });
             return None;
         };
 
-        match mi.name_or_empty() {
-            sym::feature => insert_or_error(sess, mi, &mut feature)?,
-            sym::since => insert_or_error(sess, mi, &mut since)?,
+        match param.word_or_empty_without_args().name {
+            sym::feature => insert_value_into_option_or_error(cx, &param, &mut feature)?,
+            sym::since => insert_value_into_option_or_error(cx, &param, &mut since)?,
             _ => {
-                sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
-                    span: meta.span(),
-                    item: pprust::path_to_string(&mi.path),
+                cx.emit_err(session_diagnostics::UnknownMetaItem {
+                    span: param_span,
+                    item: param.path_without_args().to_string(),
                     expected: &["feature", "since"],
                 });
                 return None;
@@ -267,9 +259,9 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
     let feature = match feature {
         Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
         Some(_bad_feature) => {
-            Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
+            Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
         }
-        None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
+        None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
     };
 
     let since = if let Some(since) = since {
@@ -278,11 +270,11 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
         } else if let Some(version) = parse_version(since) {
             StableSince::Version(version)
         } else {
-            sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span() });
+            cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
             StableSince::Err
         }
     } else {
-        sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span() });
+        cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
         StableSince::Err
     };
 
@@ -295,46 +287,48 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol,
     }
 }
 
-/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
+// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
 /// attribute, and return the feature name and its stability information.
-fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, StabilityLevel)> {
-    let metas = attr.meta_item_list()?;
-
+pub(crate) fn parse_unstability(
+    cx: &AcceptContext<'_>,
+    args: &ArgParser<'_>,
+) -> Option<(Symbol, StabilityLevel)> {
     let mut feature = None;
     let mut reason = None;
     let mut issue = None;
     let mut issue_num = None;
     let mut is_soft = false;
     let mut implied_by = None;
-    for meta in metas {
-        let Some(mi) = meta.meta_item() else {
-            sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral {
-                span: meta.span(),
+    for param in args.list()?.mixed() {
+        let Some(param) = param.meta_item() else {
+            cx.emit_err(session_diagnostics::UnsupportedLiteral {
+                span: param.span(),
                 reason: UnsupportedLiteralReason::Generic,
                 is_bytestr: false,
-                start_point_span: sess.source_map().start_point(meta.span()),
+                start_point_span: cx.sess().source_map().start_point(param.span()),
             });
             return None;
         };
 
-        match mi.name_or_empty() {
-            sym::feature => insert_or_error(sess, mi, &mut feature)?,
-            sym::reason => insert_or_error(sess, mi, &mut reason)?,
+        let (word, args) = param.word_or_empty();
+        match word.name {
+            sym::feature => insert_value_into_option_or_error(cx, &param, &mut feature)?,
+            sym::reason => insert_value_into_option_or_error(cx, &param, &mut reason)?,
             sym::issue => {
-                insert_or_error(sess, mi, &mut issue)?;
+                insert_value_into_option_or_error(cx, &param, &mut issue)?;
 
-                // These unwraps are safe because `insert_or_error` ensures the meta item
+                // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
                 // is a name/value pair string literal.
                 issue_num = match issue.unwrap().as_str() {
                     "none" => None,
-                    issue => match issue.parse::<NonZero<u32>>() {
+                    issue_str => match issue_str.parse::<NonZero<u32>>() {
                         Ok(num) => Some(num),
                         Err(err) => {
-                            sess.dcx().emit_err(
+                            cx.emit_err(
                                 session_diagnostics::InvalidIssueString {
-                                    span: mi.span,
+                                    span: param.span(),
                                     cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
-                                        mi.name_value_literal_span().unwrap(),
+                                        args.name_value().unwrap().value_span,
                                         err.kind(),
                                     ),
                                 },
@@ -345,16 +339,16 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
                 };
             }
             sym::soft => {
-                if !mi.is_word() {
-                    sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
+                if !args.no_args() {
+                    cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() });
                 }
                 is_soft = true;
             }
-            sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
+            sym::implied_by => insert_value_into_option_or_error(cx, &param, &mut implied_by)?,
             _ => {
-                sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
-                    span: meta.span(),
-                    item: pprust::path_to_string(&mi.path),
+                cx.emit_err(session_diagnostics::UnknownMetaItem {
+                    span: param.span(),
+                    item: param.path_without_args().to_string(),
                     expected: &["feature", "reason", "issue", "soft", "implied_by"],
                 });
                 return None;
@@ -365,14 +359,13 @@ fn parse_unstability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol
     let feature = match feature {
         Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
         Some(_bad_feature) => {
-            Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span() }))
+            Err(cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span }))
         }
-        None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span() })),
+        None => Err(cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })),
     };
 
-    let issue = issue.ok_or_else(|| {
-        sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span() })
-    });
+    let issue =
+        issue.ok_or_else(|| cx.emit_err(session_diagnostics::MissingIssue { span: cx.attr_span }));
 
     match (feature, issue) {
         (Ok(feature), Ok(_)) => {
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index f4065a770484..ce42b0507ed5 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -1,36 +1,33 @@
-use rustc_ast::attr::AttributeExt;
-use rustc_attr_data_structures::TransparencyError;
+use rustc_attr_data_structures::AttributeKind;
 use rustc_span::hygiene::Transparency;
 use rustc_span::sym;
 
-pub fn find_transparency(
-    attrs: &[impl AttributeExt],
-    macro_rules: bool,
-) -> (Transparency, Option<TransparencyError>) {
-    let mut transparency = None;
-    let mut error = None;
-    for attr in attrs {
-        if attr.has_name(sym::rustc_macro_transparency) {
-            if let Some((_, old_span)) = transparency {
-                error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span()));
-                break;
-            } else if let Some(value) = attr.value_str() {
-                transparency = Some((
-                    match value {
-                        sym::transparent => Transparency::Transparent,
-                        sym::semitransparent => Transparency::SemiTransparent,
-                        sym::opaque => Transparency::Opaque,
-                        _ => {
-                            error =
-                                Some(TransparencyError::UnknownTransparency(value, attr.span()));
-                            continue;
-                        }
-                    },
-                    attr.span(),
-                ));
+use super::{AcceptContext, SingleAttributeParser};
+use crate::parser::ArgParser;
+
+pub(crate) struct TransparencyParser;
+
+// FIXME(jdonszelmann): make these proper diagnostics
+#[allow(rustc::untranslatable_diagnostic)]
+#[allow(rustc::diagnostic_outside_of_impl)]
+impl SingleAttributeParser for TransparencyParser {
+    const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency];
+
+    fn on_duplicate(cx: &crate::context::AcceptContext<'_>, first_span: rustc_span::Span) {
+        cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes");
+    }
+
+    fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        match args.name_value().and_then(|nv| nv.value_as_str()) {
+            Some(sym::transparent) => Some(Transparency::Transparent),
+            Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
+            Some(sym::opaque) => Some(Transparency::Opaque),
+            Some(other) => {
+                cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
+                None
             }
+            None => None,
         }
+        .map(AttributeKind::MacroTransparency)
     }
-    let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
-    (transparency.map_or(fallback, |t| t.0), error)
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs
index e36f7dfff5a5..05a9029c59aa 100644
--- a/compiler/rustc_attr_parsing/src/attributes/util.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/util.rs
@@ -3,22 +3,6 @@ use rustc_attr_data_structures::RustcVersion;
 use rustc_feature::is_builtin_attr_name;
 use rustc_span::{Symbol, sym};
 
-pub(crate) enum UnsupportedLiteralReason {
-    Generic,
-    CfgString,
-    CfgBoolean,
-    DeprecatedString,
-    DeprecatedKvPair,
-}
-
-pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
-    attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
-}
-
-pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
-    first_attr_value_str_by_name(attrs, sym::crate_name)
-}
-
 /// Parse a rustc version number written inside string literal in an attribute,
 /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
 /// not accepted in this position, unlike when parsing CFG_RELEASE.
@@ -34,3 +18,11 @@ pub fn parse_version(s: Symbol) -> Option<RustcVersion> {
     let patch = digits.next().unwrap_or("0").parse().ok()?;
     Some(RustcVersion { major, minor, patch })
 }
+
+pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool {
+    attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
+}
+
+pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option<Symbol> {
+    first_attr_value_str_by_name(attrs, sym::crate_name)
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
new file mode 100644
index 000000000000..3bf03f84ce8f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -0,0 +1,352 @@
+use std::cell::RefCell;
+use std::collections::BTreeMap;
+use std::ops::Deref;
+use std::sync::LazyLock;
+
+use rustc_ast::{self as ast, DelimArgs};
+use rustc_attr_data_structures::AttributeKind;
+use rustc_errors::{DiagCtxtHandle, Diagnostic};
+use rustc_feature::Features;
+use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
+use rustc_session::Session;
+use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
+
+use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
+use crate::attributes::confusables::ConfusablesParser;
+use crate::attributes::deprecation::DeprecationParser;
+use crate::attributes::repr::ReprParser;
+use crate::attributes::rustc::RustcMacroEdition2021Parser;
+use crate::attributes::stability::{
+    BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
+};
+use crate::attributes::transparency::TransparencyParser;
+use crate::attributes::{AttributeParser as _, Combine, Single};
+use crate::parser::{ArgParser, MetaItemParser};
+
+macro_rules! attribute_groups {
+    (
+        pub(crate) static $name: ident = [$($names: ty),* $(,)?];
+    ) => {
+        pub(crate) static $name: LazyLock<(
+            BTreeMap<&'static [Symbol], Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>,
+            Vec<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>
+        )> = LazyLock::new(|| {
+            let mut accepts = BTreeMap::<_, Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>::new();
+            let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>::new();
+            $(
+                {
+                    thread_local! {
+                        static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
+                    };
+
+                    for (k, v) in <$names>::ATTRIBUTES {
+                        accepts.entry(*k).or_default().push(Box::new(|cx, args| {
+                            STATE_OBJECT.with_borrow_mut(|s| {
+                                v(s, cx, args)
+                            })
+                        }));
+                    }
+
+                    finalizes.push(Box::new(|cx| {
+                        let state = STATE_OBJECT.take();
+                        state.finalize(cx)
+                    }));
+                }
+            )*
+
+            (accepts, finalizes)
+        });
+    };
+}
+
+attribute_groups!(
+    pub(crate) static ATTRIBUTE_MAPPING = [
+        // tidy-alphabetical-start
+        BodyStabilityParser,
+        ConfusablesParser,
+        ConstStabilityParser,
+        StabilityParser,
+        // tidy-alphabetical-end
+
+        // tidy-alphabetical-start
+        Combine<AllowConstFnUnstableParser>,
+        Combine<AllowInternalUnstableParser>,
+        Combine<ReprParser>,
+        // tidy-alphabetical-end
+
+        // tidy-alphabetical-start
+        Single<ConstStabilityIndirectParser>,
+        Single<DeprecationParser>,
+        Single<RustcMacroEdition2021Parser>,
+        Single<TransparencyParser>,
+        // tidy-alphabetical-end
+    ];
+);
+
+/// Context given to every attribute parser when accepting
+///
+/// Gives [`AttributeParser`]s enough information to create errors, for example.
+pub(crate) struct AcceptContext<'a> {
+    pub(crate) group_cx: &'a FinalizeContext<'a>,
+    /// The span of the attribute currently being parsed
+    pub(crate) attr_span: Span,
+}
+
+impl<'a> AcceptContext<'a> {
+    pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
+        if self.limit_diagnostics {
+            self.dcx().create_err(diag).delay_as_bug()
+        } else {
+            self.dcx().emit_err(diag)
+        }
+    }
+}
+
+impl<'a> Deref for AcceptContext<'a> {
+    type Target = FinalizeContext<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.group_cx
+    }
+}
+
+/// Context given to every attribute parser during finalization.
+///
+/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create errors, for example.
+pub(crate) struct FinalizeContext<'a> {
+    /// The parse context, gives access to the session and the
+    /// diagnostics context.
+    pub(crate) cx: &'a AttributeParser<'a>,
+    /// The span of the syntactical component this attribute was applied to
+    pub(crate) target_span: Span,
+}
+
+impl<'a> Deref for FinalizeContext<'a> {
+    type Target = AttributeParser<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.cx
+    }
+}
+
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum OmitDoc {
+    Lower,
+    Skip,
+}
+
+/// Context created once, for example as part of the ast lowering
+/// context, through which all attributes can be lowered.
+pub struct AttributeParser<'sess> {
+    #[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
+    tools: Vec<Symbol>,
+    sess: &'sess Session,
+    features: Option<&'sess Features>,
+
+    /// *only* parse attributes with this symbol.
+    ///
+    /// Used in cases where we want the lowering infrastructure for
+    /// parse just a single attribute.
+    parse_only: Option<Symbol>,
+
+    /// Can be used to instruct parsers to reduce the number of diagnostics it emits.
+    /// Useful when using `parse_limited` and you know the attr will be reparsed later.
+    pub(crate) limit_diagnostics: bool,
+}
+
+impl<'sess> AttributeParser<'sess> {
+    /// This method allows you to parse attributes *before* you have access to features or tools.
+    /// One example where this is necessary, is to parse `feature` attributes themselves for
+    /// example.
+    ///
+    /// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`.
+    /// Some attributes require access to features to parse, which would crash if you tried to do so
+    /// through [`parse_limited`](Self::parse_limited).
+    ///
+    /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
+    /// that symbol are picked out of the list of instructions and parsed. Those are returned.
+    pub fn parse_limited(
+        sess: &'sess Session,
+        attrs: &[ast::Attribute],
+        sym: Symbol,
+        target_span: Span,
+        limit_diagnostics: bool,
+    ) -> Option<Attribute> {
+        let mut parsed = Self {
+            sess,
+            features: None,
+            tools: Vec::new(),
+            parse_only: Some(sym),
+            limit_diagnostics,
+        }
+        .parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
+
+        assert!(parsed.len() <= 1);
+
+        parsed.pop()
+    }
+
+    pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
+        Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
+    }
+
+    pub(crate) fn sess(&self) -> &'sess Session {
+        self.sess
+    }
+
+    pub(crate) fn features(&self) -> &'sess Features {
+        self.features.expect("features not available at this point in the compiler")
+    }
+
+    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
+        self.sess.dcx()
+    }
+
+    /// Parse a list of attributes.
+    ///
+    /// `target_span` is the span of the thing this list of attributes is applied to,
+    /// and when `omit_doc` is set, doc attributes are filtered out.
+    pub fn parse_attribute_list<'a>(
+        &'a self,
+        attrs: &'a [ast::Attribute],
+        target_span: Span,
+        omit_doc: OmitDoc,
+
+        lower_span: impl Copy + Fn(Span) -> Span,
+    ) -> Vec<Attribute> {
+        let mut attributes = Vec::new();
+
+        let group_cx = FinalizeContext { cx: self, target_span };
+
+        for attr in attrs {
+            // if we're only looking for a single attribute,
+            // skip all the ones we don't care about
+            if let Some(expected) = self.parse_only {
+                if attr.name_or_empty() != expected {
+                    continue;
+                }
+            }
+
+            // sometimes, for example for `#![doc = include_str!("readme.md")]`,
+            // doc still contains a non-literal. You might say, when we're lowering attributes
+            // that's expanded right? But no, sometimes, when parsing attributes on macros,
+            // we already use the lowering logic and these are still there. So, when `omit_doc`
+            // is set we *also* want to ignore these
+            if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc {
+                continue;
+            }
+
+            match &attr.kind {
+                ast::AttrKind::DocComment(comment_kind, symbol) => {
+                    if omit_doc == OmitDoc::Skip {
+                        continue;
+                    }
+
+                    attributes.push(Attribute::Parsed(AttributeKind::DocComment {
+                        style: attr.style,
+                        kind: *comment_kind,
+                        span: lower_span(attr.span),
+                        comment: *symbol,
+                    }))
+                }
+                // // FIXME: make doc attributes go through a proper attribute parser
+                // ast::AttrKind::Normal(n) if n.name_or_empty() == sym::doc => {
+                //     let p = GenericMetaItemParser::from_attr(&n, self.dcx());
+                //
+                //     attributes.push(Attribute::Parsed(AttributeKind::DocComment {
+                //         style: attr.style,
+                //         kind: CommentKind::Line,
+                //         span: attr.span,
+                //         comment: p.args().name_value(),
+                //     }))
+                // }
+                ast::AttrKind::Normal(n) => {
+                    let parser = MetaItemParser::from_attr(n, self.dcx());
+                    let (path, args) = parser.deconstruct();
+                    let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
+
+                    if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
+                        for f in accepts {
+                            let cx = AcceptContext {
+                                group_cx: &group_cx,
+                                attr_span: lower_span(attr.span),
+                            };
+
+                            f(&cx, &args)
+                        }
+                    } else {
+                        // if we're here, we must be compiling a tool attribute... Or someone forgot to
+                        // parse their fancy new attribute. Let's warn them in any case. If you are that
+                        // person, and you really your attribute should remain unparsed, carefully read the
+                        // documentation in this module and if you still think so you can add an exception
+                        // to this assertion.
+
+                        // FIXME(jdonszelmann): convert other attributes, and check with this that
+                        // we caught em all
+                        // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
+                        // assert!(
+                        //     self.tools.contains(&parts[0]) || true,
+                        //     // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
+                        //     "attribute {path} wasn't parsed and isn't a know tool attribute",
+                        // );
+
+                        attributes.push(Attribute::Unparsed(Box::new(AttrItem {
+                            path: AttrPath::from_ast(&n.item.path),
+                            args: self.lower_attr_args(&n.item.args, lower_span),
+                            id: HashIgnoredAttrId { attr_id: attr.id },
+                            style: attr.style,
+                            span: lower_span(attr.span),
+                        })));
+                    }
+                }
+            }
+        }
+
+        let mut parsed_attributes = Vec::new();
+        for f in &ATTRIBUTE_MAPPING.1 {
+            if let Some(attr) = f(&group_cx) {
+                parsed_attributes.push(Attribute::Parsed(attr));
+            }
+        }
+
+        attributes.extend(parsed_attributes);
+
+        attributes
+    }
+
+    fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
+        match args {
+            ast::AttrArgs::Empty => AttrArgs::Empty,
+            ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs {
+                dspan: args.dspan,
+                delim: args.delim,
+                tokens: args.tokens.clone(),
+            }),
+            // This is an inert key-value attribute - it will never be visible to macros
+            // after it gets lowered to HIR. Therefore, we can extract literals to handle
+            // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
+            ast::AttrArgs::Eq { eq_span, expr } => {
+                // In valid code the value always ends up as a single literal. Otherwise, a dummy
+                // literal suffices because the error is handled elsewhere.
+                let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
+                    && let Ok(lit) =
+                        ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
+                {
+                    lit
+                } else {
+                    let guar = self.dcx().span_delayed_bug(
+                        args.span().unwrap_or(DUMMY_SP),
+                        "expr in place where literal is expected (builtin attr parsing)",
+                    );
+                    ast::MetaItemLit {
+                        symbol: sym::dummy,
+                        suffix: None,
+                        kind: ast::LitKind::Err(guar),
+                        span: DUMMY_SP,
+                    }
+                };
+                AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs
index a1264a6875f6..249e71ef70dc 100644
--- a/compiler/rustc_attr_parsing/src/lib.rs
+++ b/compiler/rustc_attr_parsing/src/lib.rs
@@ -1,22 +1,96 @@
-//! Functions and types dealing with attributes and meta items.
+//! Centralized logic for parsing and attributes.
 //!
-//! FIXME(Centril): For now being, much of the logic is still in `rustc_ast::attr`.
-//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
-//! to this crate.
+//! Part of a series of crates:
+//! - rustc_attr_data_structures: contains types that the parsers parse into
+//! - rustc_attr_parsing: this crate
+//! - (in the future): rustc_attr_validation
+//!
+//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229).
+//! There used to be only one definition of attributes in the compiler: `ast::Attribute`.
+//! These were then parsed or validated or both in places distributed all over the compiler.
+//! This was a mess...
+//!
+//! Attributes are markers on items.
+//! Many of them are actually attribute-like proc-macros, and are expanded to some other rust syntax.
+//! This could either be a user provided proc macro, or something compiler provided.
+//! `derive` is an example of one that the compiler provides.
+//! These are built-in, but they have a valid expansion to Rust tokens and are thus called "active".
+//! I personally like calling these *active* compiler-provided attributes, built-in *macros*,
+//! because they still expand, and this helps to differentiate them from built-in *attributes*.
+//! However, I'll be the first to admit that the naming here can be confusing.
+//!
+//! The alternative to active attributes, are inert attributes.
+//! These can occur in user code (proc-macro helper attributes).
+//! But what's important is, many built-in attributes are inert like this.
+//! There is nothing they expand to during the macro expansion process,
+//! sometimes because they literally cannot expand to something that is valid Rust.
+//! They are really just markers to guide the compilation process.
+//! An example is `#[inline(...)]` which changes how code for functions is generated.
+//!
+//! ```text
+//!                      Active                 Inert
+//!              ┌──────────────────────┬──────────────────────┐
+//!              │     (mostly in)      │    these are parsed  │
+//!              │ rustc_builtin_macros │        here!         │
+//!              │                      │                      │
+//!              │                      │                      │
+//!              │    #[derive(...)]    │    #[stable()]       │
+//!     Built-in │    #[cfg()]          │    #[inline()]       │
+//!              │    #[cfg_attr()]     │    #[repr()]         │
+//!              │                      │                      │
+//!              │                      │                      │
+//!              │                      │                      │
+//!              ├──────────────────────┼──────────────────────┤
+//!              │                      │                      │
+//!              │                      │                      │
+//!              │                      │       `b` in         │
+//!              │                      │ #[proc_macro_derive( │
+//! User created │ #[proc_macro_attr()] │    a,                │
+//!              │                      │    attributes(b)     │
+//!              │                      │ ]                    │
+//!              │                      │                      │
+//!              │                      │                      │
+//!              │                      │                      │
+//!              └──────────────────────┴──────────────────────┘
+//! ```
+//!
+//! In this crate, syntactical attributes (sequences of tokens that look like
+//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items.
+//! Multiple syntactic attributes might influence a single semantic attribute. For example,
+//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define
+//! a "stability" of an item. So, the stability attribute has an
+//! [`AttributeParser`](attributes::AttributeParser) that recognizes both the `#[stable()]`
+//! and `#[unstable()]` syntactic attributes, and at the end produce a single [`AttributeKind::Stability`].
+//!
+//! As a rule of thumb, when a syntactical attribute can be applied more than once, they should be
+//! combined into a single semantic attribute. For example:
+//!
+//! ```
+//! #[repr(C)]
+//! #[repr(packed)]
+//! struct Meow {}
+//! ```
+//!
+//! should result in a single `AttributeKind::Repr` containing a list of repr annotations, in this
+//! case `C` and `packed`. This is equivalent to writing `#[repr(C, packed)]` in a single
+//! syntactical annotation.
 
 // tidy-alphabetical-start
 #![allow(internal_features)]
 #![doc(rust_logo)]
 #![feature(let_chains)]
 #![feature(rustdoc_internals)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
+#[macro_use]
 mod attributes;
+mod context;
+pub mod parser;
 mod session_diagnostics;
 
-pub use attributes::*;
+pub use attributes::cfg::*;
+pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version};
+pub use context::{AttributeParser, OmitDoc};
 pub use rustc_attr_data_structures::*;
-pub use util::{find_crate_name, is_builtin_attr, parse_version};
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs
new file mode 100644
index 000000000000..384fae59873f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/parser.rs
@@ -0,0 +1,598 @@
+//! This is in essence an (improved) duplicate of `rustc_ast/attr/mod.rs`.
+//! That module is intended to be deleted in its entirety.
+//!
+//! FIXME(jdonszelmann): delete `rustc_ast/attr/mod.rs`
+
+use std::fmt::{Debug, Display};
+use std::iter::Peekable;
+
+use rustc_ast::token::{self, Delimiter, Token};
+use rustc_ast::tokenstream::{TokenStreamIter, TokenTree};
+use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
+use rustc_ast_pretty::pprust;
+use rustc_errors::DiagCtxtHandle;
+use rustc_hir::{self as hir, AttrPath};
+use rustc_span::symbol::{Ident, kw, sym};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
+
+pub struct SegmentIterator<'a> {
+    offset: usize,
+    path: &'a PathParser<'a>,
+}
+
+impl<'a> Iterator for SegmentIterator<'a> {
+    type Item = &'a Ident;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.offset >= self.path.len() {
+            return None;
+        }
+
+        let res = match self.path {
+            PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident,
+            PathParser::Attr(attr_path) => &attr_path.segments[self.offset],
+        };
+
+        self.offset += 1;
+        Some(res)
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum PathParser<'a> {
+    Ast(&'a Path),
+    Attr(AttrPath),
+}
+
+impl<'a> PathParser<'a> {
+    pub fn get_attribute_path(&self) -> hir::AttrPath {
+        AttrPath {
+            segments: self.segments().copied().collect::<Vec<_>>().into_boxed_slice(),
+            span: self.span(),
+        }
+    }
+
+    pub fn segments(&'a self) -> impl Iterator<Item = &'a Ident> {
+        SegmentIterator { offset: 0, path: self }
+    }
+
+    pub fn span(&self) -> Span {
+        match self {
+            PathParser::Ast(path) => path.span,
+            PathParser::Attr(attr_path) => attr_path.span,
+        }
+    }
+
+    pub fn len(&self) -> usize {
+        match self {
+            PathParser::Ast(path) => path.segments.len(),
+            PathParser::Attr(attr_path) => attr_path.segments.len(),
+        }
+    }
+
+    pub fn segments_is(&self, segments: &[Symbol]) -> bool {
+        self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b)
+    }
+
+    pub fn word(&self) -> Option<Ident> {
+        (self.len() == 1).then(|| **self.segments().next().as_ref().unwrap())
+    }
+
+    pub fn word_or_empty(&self) -> Ident {
+        self.word().unwrap_or_else(Ident::empty)
+    }
+
+    /// Asserts that this MetaItem is some specific word.
+    ///
+    /// See [`word`](Self::word) for examples of what a word is.
+    pub fn word_is(&self, sym: Symbol) -> bool {
+        self.word().map(|i| i.name == sym).unwrap_or(false)
+    }
+}
+
+impl Display for PathParser<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)),
+            PathParser::Attr(attr_path) => write!(f, "{attr_path}"),
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+#[must_use]
+pub enum ArgParser<'a> {
+    NoArgs,
+    List(MetaItemListParser<'a>),
+    NameValue(NameValueParser),
+}
+
+impl<'a> ArgParser<'a> {
+    pub fn span(&self) -> Option<Span> {
+        match self {
+            Self::NoArgs => None,
+            Self::List(l) => Some(l.span),
+            Self::NameValue(n) => Some(n.value_span.with_lo(n.eq_span.lo())),
+        }
+    }
+
+    pub fn from_attr_args(value: &'a AttrArgs, dcx: DiagCtxtHandle<'a>) -> Self {
+        match value {
+            AttrArgs::Empty => Self::NoArgs,
+            AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
+                Self::List(MetaItemListParser::new(args, dcx))
+            }
+            AttrArgs::Delimited(args) => {
+                Self::List(MetaItemListParser { sub_parsers: vec![], span: args.dspan.entire() })
+            }
+            AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
+                eq_span: *eq_span,
+                value: expr_to_lit(dcx, &expr, *eq_span),
+                value_span: expr.span,
+            }),
+        }
+    }
+
+    /// Asserts that this MetaItem is a list
+    ///
+    /// Some examples:
+    ///
+    /// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list
+    /// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list
+    pub fn list(&self) -> Option<&MetaItemListParser<'a>> {
+        match self {
+            Self::List(l) => Some(l),
+            Self::NameValue(_) | Self::NoArgs => None,
+        }
+    }
+
+    /// Asserts that this MetaItem is a name-value pair.
+    ///
+    /// Some examples:
+    ///
+    /// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair,
+    ///   where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path
+    ///   to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is
+    ///   there
+    /// - `#[doc = "hello"]`: `doc = "hello`  is also a name value pair
+    pub fn name_value(&self) -> Option<&NameValueParser> {
+        match self {
+            Self::NameValue(n) => Some(n),
+            Self::List(_) | Self::NoArgs => None,
+        }
+    }
+
+    /// Asserts that there are no arguments
+    pub fn no_args(&self) -> bool {
+        matches!(self, Self::NoArgs)
+    }
+}
+
+/// Inside lists, values could be either literals, or more deeply nested meta items.
+/// This enum represents that.
+///
+/// Choose which one you want using the provided methods.
+#[derive(Debug, Clone)]
+pub enum MetaItemOrLitParser<'a> {
+    MetaItemParser(MetaItemParser<'a>),
+    Lit(MetaItemLit),
+    Err(Span, ErrorGuaranteed),
+}
+
+impl<'a> MetaItemOrLitParser<'a> {
+    pub fn span(&self) -> Span {
+        match self {
+            MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => {
+                generic_meta_item_parser.span()
+            }
+            MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
+            MetaItemOrLitParser::Err(span, _) => *span,
+        }
+    }
+
+    pub fn lit(&self) -> Option<&MetaItemLit> {
+        match self {
+            MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit),
+            _ => None,
+        }
+    }
+
+    pub fn meta_item(&self) -> Option<&MetaItemParser<'a>> {
+        match self {
+            MetaItemOrLitParser::MetaItemParser(parser) => Some(parser),
+            _ => None,
+        }
+    }
+}
+
+/// Utility that deconstructs a MetaItem into usable parts.
+///
+/// MetaItems are syntactically extremely flexible, but specific attributes want to parse
+/// them in custom, more restricted ways. This can be done using this struct.
+///
+/// MetaItems consist of some path, and some args. The args could be empty. In other words:
+///
+/// - `name` -> args are empty
+/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses
+/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the
+///   `= value` part
+///
+/// The syntax of MetaItems can be found at <https://doc.rust-lang.org/reference/attributes.html>
+#[derive(Clone)]
+pub struct MetaItemParser<'a> {
+    path: PathParser<'a>,
+    args: ArgParser<'a>,
+}
+
+impl<'a> Debug for MetaItemParser<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("MetaItemParser")
+            .field("path", &self.path)
+            .field("args", &self.args)
+            .finish()
+    }
+}
+
+impl<'a> MetaItemParser<'a> {
+    /// Create a new parser from a [`NormalAttr`], which is stored inside of any
+    /// [`ast::Attribute`](rustc_ast::Attribute)
+    pub fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self {
+        Self {
+            path: PathParser::Ast(&attr.item.path),
+            args: ArgParser::from_attr_args(&attr.item.args, dcx),
+        }
+    }
+}
+
+impl<'a> MetaItemParser<'a> {
+    pub fn span(&self) -> Span {
+        if let Some(other) = self.args.span() {
+            self.path.span().with_hi(other.hi())
+        } else {
+            self.path.span()
+        }
+    }
+
+    /// Gets just the path, without the args.
+    pub fn path_without_args(&self) -> PathParser<'a> {
+        self.path.clone()
+    }
+
+    /// Gets just the args parser, without caring about the path.
+    pub fn args(&self) -> &ArgParser<'a> {
+        &self.args
+    }
+
+    pub fn deconstruct(&self) -> (PathParser<'a>, &ArgParser<'a>) {
+        (self.path_without_args(), self.args())
+    }
+
+    /// Asserts that this MetaItem starts with a path. Some examples:
+    ///
+    /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path
+    /// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path
+    /// - `#[inline]`: `inline` is a single segment path
+    pub fn path(&self) -> (PathParser<'a>, &ArgParser<'a>) {
+        self.deconstruct()
+    }
+
+    /// Asserts that this MetaItem starts with a word, or single segment path.
+    /// Doesn't return the args parser.
+    ///
+    /// For examples. see [`Self::word`]
+    pub fn word_without_args(&self) -> Option<Ident> {
+        Some(self.word()?.0)
+    }
+
+    /// Like [`word`](Self::word), but returns an empty symbol instead of None
+    pub fn word_or_empty_without_args(&self) -> Ident {
+        self.word_or_empty().0
+    }
+
+    /// Asserts that this MetaItem starts with a word, or single segment path.
+    ///
+    /// Some examples:
+    /// - `#[inline]`: `inline` is a word
+    /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path,
+    ///   and not a word and should instead be parsed using [`path`](Self::path)
+    pub fn word(&self) -> Option<(Ident, &ArgParser<'a>)> {
+        let (path, args) = self.deconstruct();
+        Some((path.word()?, args))
+    }
+
+    /// Like [`word`](Self::word), but returns an empty symbol instead of None
+    pub fn word_or_empty(&self) -> (Ident, &ArgParser<'a>) {
+        let (path, args) = self.deconstruct();
+        (path.word().unwrap_or(Ident::empty()), args)
+    }
+
+    /// Asserts that this MetaItem starts with some specific word.
+    ///
+    /// See [`word`](Self::word) for examples of what a word is.
+    pub fn word_is(&self, sym: Symbol) -> Option<&ArgParser<'a>> {
+        self.path_without_args().word_is(sym).then(|| self.args())
+    }
+
+    /// Asserts that this MetaItem starts with some specific path.
+    ///
+    /// See [`word`](Self::path) for examples of what a word is.
+    pub fn path_is(&self, segments: &[Symbol]) -> Option<&ArgParser<'a>> {
+        self.path_without_args().segments_is(segments).then(|| self.args())
+    }
+}
+
+#[derive(Clone)]
+pub struct NameValueParser {
+    pub eq_span: Span,
+    value: MetaItemLit,
+    pub value_span: Span,
+}
+
+impl Debug for NameValueParser {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("NameValueParser")
+            .field("eq_span", &self.eq_span)
+            .field("value", &self.value)
+            .field("value_span", &self.value_span)
+            .finish()
+    }
+}
+
+impl NameValueParser {
+    pub fn value_as_lit(&self) -> &MetaItemLit {
+        &self.value
+    }
+
+    pub fn value_as_str(&self) -> Option<Symbol> {
+        self.value_as_lit().kind.str()
+    }
+}
+
+fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr, span: Span) -> MetaItemLit {
+    // In valid code the value always ends up as a single literal. Otherwise, a dummy
+    // literal suffices because the error is handled elsewhere.
+    if let ExprKind::Lit(token_lit) = expr.kind
+        && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span)
+    {
+        lit
+    } else {
+        let guar = dcx.span_delayed_bug(
+            span,
+            "expr in place where literal is expected (builtin attr parsing)",
+        );
+        MetaItemLit { symbol: sym::dummy, suffix: None, kind: LitKind::Err(guar), span }
+    }
+}
+
+struct MetaItemListParserContext<'a> {
+    // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside
+    inside_delimiters: Peekable<TokenStreamIter<'a>>,
+    dcx: DiagCtxtHandle<'a>,
+}
+
+impl<'a> MetaItemListParserContext<'a> {
+    fn done(&mut self) -> bool {
+        self.inside_delimiters.peek().is_none()
+    }
+
+    fn next_path(&mut self) -> Option<AttrPath> {
+        // FIXME: Share code with `parse_path`.
+        let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt));
+
+        match tt.as_deref()? {
+            &TokenTree::Token(
+                Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
+                _,
+            ) => {
+                // here we have either an ident or pathsep `::`.
+
+                let mut segments = if let &token::Ident(name, _) = kind {
+                    // when we lookahead another pathsep, more path's coming
+                    if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
+                        self.inside_delimiters.peek()
+                    {
+                        self.inside_delimiters.next();
+                        vec![Ident::new(name, span)]
+                    } else {
+                        // else we have a single identifier path, that's all
+                        return Some(AttrPath {
+                            segments: vec![Ident::new(name, span)].into_boxed_slice(),
+                            span,
+                        });
+                    }
+                } else {
+                    // if `::` is all we get, we just got a path root
+                    vec![Ident::new(kw::PathRoot, span)]
+                };
+
+                // one segment accepted. accept n more
+                loop {
+                    // another ident?
+                    if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
+                        self.inside_delimiters
+                            .next()
+                            .map(|tt| TokenTree::uninterpolate(tt))
+                            .as_deref()
+                    {
+                        segments.push(Ident::new(name, span));
+                    } else {
+                        return None;
+                    }
+                    // stop unless we see another `::`
+                    if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
+                        self.inside_delimiters.peek()
+                    {
+                        self.inside_delimiters.next();
+                    } else {
+                        break;
+                    }
+                }
+                let span = span.with_hi(segments.last().unwrap().span.hi());
+                Some(AttrPath { segments: segments.into_boxed_slice(), span })
+            }
+            TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => {
+                None
+            }
+            _ => {
+                // malformed attributes can get here. We can't crash, but somewhere else should've
+                // already warned for this.
+                None
+            }
+        }
+    }
+
+    fn value(&mut self) -> Option<MetaItemLit> {
+        match self.inside_delimiters.next() {
+            Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
+                MetaItemListParserContext {
+                    inside_delimiters: inner_tokens.iter().peekable(),
+                    dcx: self.dcx,
+                }
+                .value()
+            }
+            Some(TokenTree::Token(token, _)) => MetaItemLit::from_token(token),
+            _ => None,
+        }
+    }
+
+    /// parses one element on the inside of a list attribute like `#[my_attr( <insides> )]`
+    ///
+    /// parses a path followed be either:
+    /// 1. nothing (a word attr)
+    /// 2. a parenthesized list
+    /// 3. an equals sign and a literal (name-value)
+    ///
+    /// Can also parse *just* a literal. This is for cases like as `#[my_attr("literal")]`
+    /// where no path is given before the literal
+    ///
+    /// Some exceptions too for interpolated attributes which are already pre-processed
+    fn next(&mut self) -> Option<MetaItemOrLitParser<'a>> {
+        // a list element is either a literal
+        if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek()
+            && let Some(lit) = MetaItemLit::from_token(token)
+        {
+            self.inside_delimiters.next();
+            return Some(MetaItemOrLitParser::Lit(lit));
+        } else if let Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) =
+            self.inside_delimiters.peek()
+        {
+            self.inside_delimiters.next();
+            return MetaItemListParserContext {
+                inside_delimiters: inner_tokens.iter().peekable(),
+                dcx: self.dcx,
+            }
+            .next();
+        }
+
+        // or a path.
+        let path = self.next_path()?;
+
+        // Paths can be followed by:
+        // - `(more meta items)` (another list)
+        // - `= lit` (a name-value)
+        // - nothing
+        Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() {
+            Some(TokenTree::Delimited(dspan, _, Delimiter::Parenthesis, inner_tokens)) => {
+                self.inside_delimiters.next();
+
+                MetaItemParser {
+                    path: PathParser::Attr(path),
+                    args: ArgParser::List(MetaItemListParser::new_tts(
+                        inner_tokens.iter(),
+                        dspan.entire(),
+                        self.dcx,
+                    )),
+                }
+            }
+            Some(TokenTree::Delimited(_, ..)) => {
+                self.inside_delimiters.next();
+                // self.dcx.span_delayed_bug(span.entire(), "wrong delimiters");
+                return None;
+            }
+            Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => {
+                self.inside_delimiters.next();
+                let value = self.value()?;
+                MetaItemParser {
+                    path: PathParser::Attr(path),
+                    args: ArgParser::NameValue(NameValueParser {
+                        eq_span: *span,
+                        value_span: value.span,
+                        value,
+                    }),
+                }
+            }
+            _ => MetaItemParser { path: PathParser::Attr(path), args: ArgParser::NoArgs },
+        }))
+    }
+
+    fn parse(mut self, span: Span) -> MetaItemListParser<'a> {
+        let mut sub_parsers = Vec::new();
+
+        while !self.done() {
+            let Some(n) = self.next() else {
+                continue;
+            };
+            sub_parsers.push(n);
+
+            match self.inside_delimiters.peek() {
+                None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {
+                    self.inside_delimiters.next();
+                }
+                Some(_) => {}
+            }
+        }
+
+        MetaItemListParser { sub_parsers, span }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct MetaItemListParser<'a> {
+    sub_parsers: Vec<MetaItemOrLitParser<'a>>,
+    pub span: Span,
+}
+
+impl<'a> MetaItemListParser<'a> {
+    fn new(delim: &'a DelimArgs, dcx: DiagCtxtHandle<'a>) -> MetaItemListParser<'a> {
+        MetaItemListParser::new_tts(delim.tokens.iter(), delim.dspan.entire(), dcx)
+    }
+
+    fn new_tts(tts: TokenStreamIter<'a>, span: Span, dcx: DiagCtxtHandle<'a>) -> Self {
+        MetaItemListParserContext { inside_delimiters: tts.peekable(), dcx }.parse(span)
+    }
+
+    /// Lets you pick and choose as what you want to parse each element in the list
+    pub fn mixed<'s>(&'s self) -> impl Iterator<Item = &'s MetaItemOrLitParser<'a>> + 's {
+        self.sub_parsers.iter()
+    }
+
+    pub fn len(&self) -> usize {
+        self.sub_parsers.len()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// Asserts that every item in the list is another list starting with a word.
+    ///
+    /// See [`MetaItemParser::word`] for examples of words.
+    pub fn all_word_list<'s>(&'s self) -> Option<Vec<(Ident, &'s ArgParser<'a>)>> {
+        self.mixed().map(|i| i.meta_item()?.word()).collect()
+    }
+
+    /// Asserts that every item in the list is another list starting with a full path.
+    ///
+    /// See [`MetaItemParser::path`] for examples of paths.
+    pub fn all_path_list<'s>(&'s self) -> Option<Vec<(PathParser<'a>, &'s ArgParser<'a>)>> {
+        self.mixed().map(|i| Some(i.meta_item()?.path())).collect()
+    }
+
+    /// Returns Some if the list contains only a single element.
+    ///
+    /// Inside the Some is the parser to parse this single element.
+    pub fn single(&self) -> Option<&MetaItemOrLitParser<'a>> {
+        let mut iter = self.mixed();
+        iter.next().filter(|_| iter.next().is_none())
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index b1d8ec497740..9d34b807ac2f 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -6,9 +6,16 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{Span, Symbol};
 
-use crate::attributes::util::UnsupportedLiteralReason;
 use crate::fluent_generated as fluent;
 
+pub(crate) enum UnsupportedLiteralReason {
+    Generic,
+    CfgString,
+    CfgBoolean,
+    DeprecatedString,
+    DeprecatedKvPair,
+}
+
 #[derive(Diagnostic)]
 #[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)]
 pub(crate) struct ExpectedOneCfgPattern {
@@ -39,6 +46,21 @@ pub(crate) struct MultipleItem {
 pub(crate) struct IncorrectMetaItem {
     #[primary_span]
     pub span: Span,
+
+    #[subdiagnostic]
+    pub suggestion: Option<IncorrectMetaItemSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    attr_parsing_incorrect_meta_item_suggestion,
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct IncorrectMetaItemSuggestion {
+    #[suggestion_part(code = "\"")]
+    pub lo: Span,
+    #[suggestion_part(code = "\"")]
+    pub hi: Span,
 }
 
 /// Error code: E0541
@@ -204,21 +226,27 @@ pub(crate) struct UnsupportedLiteral {
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
     fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
-        let mut diag = Diag::new(dcx, level, match self.reason {
-            UnsupportedLiteralReason::Generic => fluent::attr_parsing_unsupported_literal_generic,
-            UnsupportedLiteralReason::CfgString => {
-                fluent::attr_parsing_unsupported_literal_cfg_string
-            }
-            UnsupportedLiteralReason::CfgBoolean => {
-                fluent::attr_parsing_unsupported_literal_cfg_boolean
-            }
-            UnsupportedLiteralReason::DeprecatedString => {
-                fluent::attr_parsing_unsupported_literal_deprecated_string
-            }
-            UnsupportedLiteralReason::DeprecatedKvPair => {
-                fluent::attr_parsing_unsupported_literal_deprecated_kv_pair
-            }
-        });
+        let mut diag = Diag::new(
+            dcx,
+            level,
+            match self.reason {
+                UnsupportedLiteralReason::Generic => {
+                    fluent::attr_parsing_unsupported_literal_generic
+                }
+                UnsupportedLiteralReason::CfgString => {
+                    fluent::attr_parsing_unsupported_literal_cfg_string
+                }
+                UnsupportedLiteralReason::CfgBoolean => {
+                    fluent::attr_parsing_unsupported_literal_cfg_boolean
+                }
+                UnsupportedLiteralReason::DeprecatedString => {
+                    fluent::attr_parsing_unsupported_literal_deprecated_string
+                }
+                UnsupportedLiteralReason::DeprecatedKvPair => {
+                    fluent::attr_parsing_unsupported_literal_deprecated_kv_pair
+                }
+            },
+        );
         diag.span(self.span);
         diag.code(E0565);
         if self.is_bytestr {
@@ -331,13 +359,6 @@ pub(crate) struct RustcPromotablePairing {
     pub span: Span,
 }
 
-#[derive(Diagnostic)]
-#[diag(attr_parsing_rustc_const_stable_indirect_pairing)]
-pub(crate) struct RustcConstStableIndirectPairing {
-    #[primary_span]
-    pub span: Span,
-}
-
 #[derive(Diagnostic)]
 #[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)]
 pub(crate) struct RustcAllowedUnstablePairing {
@@ -417,3 +438,44 @@ pub(crate) struct UnknownVersionLiteral {
     #[primary_span]
     pub span: Span,
 }
+
+// FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated.
+#[derive(Diagnostic)]
+#[diag(attr_parsing_unused_multiple)]
+pub(crate) struct UnusedMultiple {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub this: Span,
+    #[note]
+    pub other: Span,
+    pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_stability_outside_std, code = E0734)]
+pub(crate) struct StabilityOutsideStd {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_empty_confusables)]
+pub(crate) struct EmptyConfusables {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_repr_ident, code = E0565)]
+pub(crate) struct ReprIdent {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)]
+#[help]
+pub(crate) struct UnrecognizedReprHint {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_baked_icu_data/Cargo.toml b/compiler/rustc_baked_icu_data/Cargo.toml
index c35556dcf5bf..cb0e145386b8 100644
--- a/compiler/rustc_baked_icu_data/Cargo.toml
+++ b/compiler/rustc_baked_icu_data/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustc_baked_icu_data"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
diff --git a/compiler/rustc_baked_icu_data/src/lib.rs b/compiler/rustc_baked_icu_data/src/lib.rs
index f86a9db61c60..f3f6522f5758 100644
--- a/compiler/rustc_baked_icu_data/src/lib.rs
+++ b/compiler/rustc_baked_icu_data/src/lib.rs
@@ -23,6 +23,7 @@
 // tidy-alphabetical-start
 #![allow(elided_lifetimes_in_paths)]
 #![allow(internal_features)]
+#![allow(unreachable_pub)] // because this crate is mostly generated code
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
 // #![warn(unreachable_pub)] // don't use because this crate is mostly generated code
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml
index 89154bf2c23c..9e7d55180a23 100644
--- a/compiler/rustc_borrowck/Cargo.toml
+++ b/compiler/rustc_borrowck/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "rustc_borrowck"
 version = "0.0.0"
-edition = "2021"
+edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl
index ada20e5c614f..33b80c4b03d6 100644
--- a/compiler/rustc_borrowck/messages.ftl
+++ b/compiler/rustc_borrowck/messages.ftl
@@ -162,13 +162,6 @@ borrowck_opaque_type_lifetime_mismatch =
     .prev_lifetime_label = lifetime `{$prev}` previously used here
     .note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
 
-borrowck_opaque_type_non_generic_param =
-    expected generic {$kind} parameter, found `{$ty}`
-    .label = {STREQ($ty, "'static") ->
-        [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
-        *[other] this generic parameter must be used with a generic {$kind} parameter
-    }
-
 borrowck_partial_var_move_by_use_in_closure =
     variable {$is_partial ->
         [true] partially moved
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index 30e94b0bec70..c9be5575da5c 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -403,6 +403,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 .expect_closure();
             let span = match capture_clause {
                 rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(),
+                rustc_hir::CaptureBy::Use { use_kw } => use_kw.shrink_to_lo(),
                 rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(),
             };
             diag.span_suggestion_verbose(
diff --git a/compiler/rustc_borrowck/src/constraints/graph.rs b/compiler/rustc_borrowck/src/constraints/graph.rs
index 9d500586f084..1209d8bf5967 100644
--- a/compiler/rustc_borrowck/src/constraints/graph.rs
+++ b/compiler/rustc_borrowck/src/constraints/graph.rs
@@ -1,11 +1,8 @@
 use rustc_data_structures::graph;
 use rustc_index::IndexVec;
-use rustc_middle::mir::ConstraintCategory;
-use rustc_middle::ty::{RegionVid, VarianceDiagInfo};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty::RegionVid;
 
 use crate::constraints::{OutlivesConstraint, OutlivesConstraintIndex, OutlivesConstraintSet};
-use crate::type_check::Locations;
 
 /// The construct graph organizes the constraints by their end-points.
 /// It can be used to view a `R1: R2` constraint as either an edge `R1
@@ -23,8 +20,8 @@ pub(crate) type ReverseConstraintGraph = ConstraintGraph<Reverse>;
 /// Marker trait that controls whether a `R1: R2` constraint
 /// represents an edge `R1 -> R2` or `R2 -> R1`.
 pub(crate) trait ConstraintGraphDirection: Copy + 'static {
-    fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid;
-    fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid;
+    fn start_region(sup: RegionVid, sub: RegionVid) -> RegionVid;
+    fn end_region(sup: RegionVid, sub: RegionVid) -> RegionVid;
     fn is_normal() -> bool;
 }
 
@@ -36,12 +33,12 @@ pub(crate) trait ConstraintGraphDirection: Copy + 'static {
 pub(crate) struct Normal;
 
 impl ConstraintGraphDirection for Normal {
-    fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
-        c.sup
+    fn start_region(sup: RegionVid, _sub: RegionVid) -> RegionVid {
+        sup
     }
 
-    fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
-        c.sub
+    fn end_region(_sup: RegionVid, sub: RegionVid) -> RegionVid {
+        sub
     }
 
     fn is_normal() -> bool {
@@ -57,12 +54,12 @@ impl ConstraintGraphDirection for Normal {
 pub(crate) struct Reverse;
 
 impl ConstraintGraphDirection for Reverse {
-    fn start_region(c: &OutlivesConstraint<'_>) -> RegionVid {
-        c.sub
+    fn start_region(_sup: RegionVid, sub: RegionVid) -> RegionVid {
+        sub
     }
 
-    fn end_region(c: &OutlivesConstraint<'_>) -> RegionVid {
-        c.sup
+    fn end_region(sup: RegionVid, _sub: RegionVid) -> RegionVid {
+        sup
     }
 
     fn is_normal() -> bool {
@@ -84,7 +81,7 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
         let mut next_constraints = IndexVec::from_elem(None, &set.outlives);
 
         for (idx, constraint) in set.outlives.iter_enumerated().rev() {
-            let head = &mut first_constraints[D::start_region(constraint)];
+            let head = &mut first_constraints[D::start_region(constraint.sup, constraint.sub)];
             let next = &mut next_constraints[idx];
             debug_assert!(next.is_none());
             *next = *head;
@@ -105,63 +102,57 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
         RegionGraph::new(set, self, static_region)
     }
 
+    pub(crate) fn is_normal(&self) -> bool {
+        D::is_normal()
+    }
+
     /// Given a region `R`, iterate over all constraints `R: R1`.
-    pub(crate) fn outgoing_edges<'a, 'tcx>(
+    pub(crate) fn outgoing_edges_from_graph<'a, 'tcx>(
         &'a self,
         region_sup: RegionVid,
         constraints: &'a OutlivesConstraintSet<'tcx>,
-        static_region: RegionVid,
-    ) -> Edges<'a, 'tcx, D> {
-        //if this is the `'static` region and the graph's direction is normal,
-        //then setup the Edges iterator to return all regions #53178
-        if region_sup == static_region && D::is_normal() {
-            Edges {
-                graph: self,
-                constraints,
-                pointer: None,
-                next_static_idx: Some(0),
-                static_region,
-            }
-        } else {
-            //otherwise, just setup the iterator as normal
-            let first = self.first_constraints[region_sup];
-            Edges { graph: self, constraints, pointer: first, next_static_idx: None, static_region }
-        }
+    ) -> EdgesFromGraph<'a, 'tcx, D> {
+        EdgesFromGraph { graph: self, constraints, pointer: self.first_constraints[region_sup] }
+    }
+
+    /// Returns all regions (#53178).
+    pub(crate) fn outgoing_edges_from_static(&self) -> EdgesFromStatic {
+        EdgesFromStatic { next_static_idx: 0, end_static_idx: self.first_constraints.len() }
     }
 }
 
-pub(crate) struct Edges<'a, 'tcx, D: ConstraintGraphDirection> {
+pub(crate) struct EdgesFromGraph<'a, 'tcx, D: ConstraintGraphDirection> {
     graph: &'a ConstraintGraph<D>,
     constraints: &'a OutlivesConstraintSet<'tcx>,
     pointer: Option<OutlivesConstraintIndex>,
-    next_static_idx: Option<usize>,
-    static_region: RegionVid,
 }
 
-impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'a, 'tcx, D> {
-    type Item = OutlivesConstraint<'tcx>;
+impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for EdgesFromGraph<'a, 'tcx, D> {
+    type Item = &'a OutlivesConstraint<'tcx>;
 
     fn next(&mut self) -> Option<Self::Item> {
         if let Some(p) = self.pointer {
             self.pointer = self.graph.next_constraints[p];
+            Some(&self.constraints[p])
+        } else {
+            None
+        }
+    }
+}
+
+pub(crate) struct EdgesFromStatic {
+    next_static_idx: usize,
+    end_static_idx: usize,
+}
+
+impl Iterator for EdgesFromStatic {
+    type Item = RegionVid;
 
-            Some(self.constraints[p])
-        } else if let Some(next_static_idx) = self.next_static_idx {
-            self.next_static_idx = if next_static_idx == (self.graph.first_constraints.len() - 1) {
-                None
-            } else {
-                Some(next_static_idx + 1)
-            };
-
-            Some(OutlivesConstraint {
-                sup: self.static_region,
-                sub: next_static_idx.into(),
-                locations: Locations::All(DUMMY_SP),
-                span: DUMMY_SP,
-                category: ConstraintCategory::Internal,
-                variance_info: VarianceDiagInfo::default(),
-                from_closure: false,
-            })
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.next_static_idx < self.end_static_idx {
+            let ret = RegionVid::from_usize(self.next_static_idx);
+            self.next_static_idx += 1;
+            Some(ret)
         } else {
             None
         }
@@ -193,21 +184,38 @@ impl<'a, 'tcx, D: ConstraintGraphDirection> RegionGraph<'a, 'tcx, D> {
     /// Given a region `R`, iterate over all regions `R1` such that
     /// there exists a constraint `R: R1`.
     pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'a, 'tcx, D> {
-        Successors {
-            edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
+        // If this is the `'static` region and the graph's direction is normal,
+        // then setup the Edges iterator to return all regions (#53178).
+        if region_sup == self.static_region && D::is_normal() {
+            Successors::FromStatic(self.constraint_graph.outgoing_edges_from_static())
+        } else {
+            // Otherwise, just setup the iterator as normal.
+            Successors::FromGraph(
+                self.constraint_graph.outgoing_edges_from_graph(region_sup, self.set),
+            )
         }
     }
 }
 
-pub(crate) struct Successors<'a, 'tcx, D: ConstraintGraphDirection> {
-    edges: Edges<'a, 'tcx, D>,
+pub(crate) enum Successors<'a, 'tcx, D: ConstraintGraphDirection> {
+    FromStatic(EdgesFromStatic),
+    FromGraph(EdgesFromGraph<'a, 'tcx, D>),
 }
 
 impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Successors<'a, 'tcx, D> {
     type Item = RegionVid;
 
     fn next(&mut self) -> Option<Self::Item> {
-        self.edges.next().map(|c| D::end_region(&c))
+        match self {
+            Successors::FromStatic(edges) => {
+                // No `D::end_region` call needed here: static successors are only possible when
+                // the direction is `Normal`, so we can directly use what would be the `sub` value.
+                edges.next()
+            }
+            Successors::FromGraph(edges) => {
+                edges.next().map(|constraint| D::end_region(constraint.sup, constraint.sub))
+            }
+        }
     }
 }
 
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index 5a89f7c351cf..1f087b092346 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -1,7 +1,7 @@
 //! This file provides API for compiler consumers.
 
 use rustc_hir::def_id::LocalDefId;
-use rustc_index::{IndexSlice, IndexVec};
+use rustc_index::IndexVec;
 use rustc_middle::mir::{Body, Promoted};
 use rustc_middle::ty::TyCtxt;
 
@@ -15,6 +15,7 @@ pub use super::polonius::legacy::{
     RichLocation, RustcFacts,
 };
 pub use super::region_infer::RegionInferenceContext;
+use crate::{BorrowCheckRootCtxt, do_mir_borrowck};
 
 /// Options determining the output behavior of [`get_body_with_borrowck_facts`].
 ///
@@ -97,11 +98,9 @@ pub struct BodyWithBorrowckFacts<'tcx> {
 /// *   Polonius is highly unstable, so expect regular changes in its signature or other details.
 pub fn get_body_with_borrowck_facts(
     tcx: TyCtxt<'_>,
-    def: LocalDefId,
+    def_id: LocalDefId,
     options: ConsumerOptions,
 ) -> BodyWithBorrowckFacts<'_> {
-    let (input_body, promoted) = tcx.mir_promoted(def);
-    let input_body: &Body<'_> = &input_body.borrow();
-    let promoted: &IndexSlice<_, _> = &promoted.borrow();
-    *super::do_mir_borrowck(tcx, input_body, promoted, Some(options)).1.unwrap()
+    let mut root_cx = BorrowCheckRootCtxt::new(tcx, def_id);
+    *do_mir_borrowck(&mut root_cx, def_id, Some(options)).1.unwrap()
 }
diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs
index 263f68d6a3dc..b9ced81c46c1 100644
--- a/compiler/rustc_borrowck/src/def_use.rs
+++ b/compiler/rustc_borrowck/src/def_use.rs
@@ -77,6 +77,9 @@ pub(crate) fn categorize(context: PlaceContext) -> Option<DefUse> {
         // Debug info is neither def nor use.
         PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
 
+        // Backwards incompatible drop hint is not a use, just a marker for linting.
+        PlaceContext::NonUse(NonUseContext::BackwardIncompatibleDropHint) => None,
+
         PlaceContext::MutatingUse(MutatingUseContext::Deinit | MutatingUseContext::SetDiscriminant) => {
             bug!("These statements are not allowed in this MIR phase")
         }
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 90d12ea83285..134f30ed6f54 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -9,8 +9,8 @@ use rustc_infer::infer::{
 };
 use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::query::{
-    CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal,
-    CanonicalTypeOpProvePredicateGoal,
+    CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpDeeplyNormalizeGoal,
+    CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal,
 };
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{
@@ -109,6 +109,14 @@ impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUnivers
     }
 }
 
+impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUniverseInfo<'tcx>
+    for CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T>
+{
+    fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
+        UniverseInfo::TypeOp(Rc::new(DeeplyNormalizeQuery { canonical_query: self, base_universe }))
+    }
+}
+
 impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpAscribeUserTypeGoal<'tcx> {
     fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> {
         UniverseInfo::TypeOp(Rc::new(AscribeUserTypeQuery { canonical_query: self, base_universe }))
@@ -156,24 +164,25 @@ pub(crate) trait TypeOpInfo<'tcx> {
             return;
         };
 
-        let placeholder_region = ty::Region::new_placeholder(tcx, ty::Placeholder {
-            universe: adjusted_universe.into(),
-            bound: placeholder.bound,
-        });
-
-        let error_region =
-            if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
-                let adjusted_universe =
-                    error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
-                adjusted_universe.map(|adjusted| {
-                    ty::Region::new_placeholder(tcx, ty::Placeholder {
-                        universe: adjusted.into(),
-                        bound: error_placeholder.bound,
-                    })
-                })
-            } else {
-                None
-            };
+        let placeholder_region = ty::Region::new_placeholder(
+            tcx,
+            ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
+        );
+
+        let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) =
+            error_element
+        {
+            let adjusted_universe =
+                error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
+            adjusted_universe.map(|adjusted| {
+                ty::Region::new_placeholder(
+                    tcx,
+                    ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound },
+                )
+            })
+        } else {
+            None
+        };
 
         debug!(?placeholder_region);
 
@@ -284,6 +293,53 @@ where
     }
 }
 
+struct DeeplyNormalizeQuery<'tcx, T> {
+    canonical_query: CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T>,
+    base_universe: ty::UniverseIndex,
+}
+
+impl<'tcx, T> TypeOpInfo<'tcx> for DeeplyNormalizeQuery<'tcx, T>
+where
+    T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx,
+{
+    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> {
+        tcx.dcx().create_err(HigherRankedLifetimeError {
+            cause: Some(HigherRankedErrorCause::CouldNotNormalize {
+                value: self.canonical_query.canonical.value.value.value.to_string(),
+            }),
+            span,
+        })
+    }
+
+    fn base_universe(&self) -> ty::UniverseIndex {
+        self.base_universe
+    }
+
+    fn nice_error<'infcx>(
+        &self,
+        mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>,
+        cause: ObligationCause<'tcx>,
+        placeholder_region: ty::Region<'tcx>,
+        error_region: Option<ty::Region<'tcx>>,
+    ) -> Option<Diag<'infcx>> {
+        let (infcx, key, _) =
+            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
+        let ocx = ObligationCtxt::new(&infcx);
+
+        let (param_env, value) = key.into_parts();
+        let _ = ocx.deeply_normalize(&cause, param_env, value.value);
+
+        let diag = try_extract_error_from_fulfill_cx(
+            &ocx,
+            mbcx.mir_def_id(),
+            placeholder_region,
+            error_region,
+        )?
+        .with_dcx(mbcx.dcx());
+        Some(diag)
+    }
+}
+
 struct AscribeUserTypeQuery<'tcx> {
     canonical_query: CanonicalTypeOpAscribeUserTypeGoal<'tcx>,
     base_universe: ty::UniverseIndex,
@@ -310,7 +366,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
         let (infcx, key, _) =
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
-        type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?;
+        type_op_ascribe_user_type_with_span(&ocx, key, cause.span).ok()?;
         let diag = try_extract_error_from_fulfill_cx(
             &ocx,
             mbcx.mir_def_id(),
@@ -350,8 +406,8 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
             // started MIR borrowchecking with, so the region
             // constraints have already been taken. Use the data from
             // our `mbcx` instead.
-            |vid| mbcx.regioncx.var_infos[vid].origin,
-            |vid| mbcx.regioncx.var_infos[vid].universe,
+            |vid| RegionVariableOrigin::Nll(mbcx.regioncx.definitions[vid].origin),
+            |vid| mbcx.regioncx.definitions[vid].universe,
         )
     }
 }
@@ -431,7 +487,7 @@ fn try_extract_error_from_region_constraints<'a, 'tcx>(
     let (sub_region, cause) = info?;
 
     debug!(?sub_region, "cause = {:#?}", cause);
-    let error = match (error_region, *sub_region) {
+    let error = match (error_region, sub_region.kind()) {
         (Some(error_region), ty::ReVar(vid)) => RegionResolutionError::SubSupConflict(
             vid,
             region_var_origin(vid),
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 497d672ccb21..959cf9fa5139 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -8,22 +8,20 @@ use std::ops::ControlFlow;
 
 use either::Either;
 use hir::{ClosureKind, Path};
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{Map, Visitor, walk_block, walk_expr};
+use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};
 use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField};
 use rustc_middle::bug;
 use rustc_middle::hir::nested_filter::OnlyBodies;
-use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
     self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
     FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,
-    Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
-    TerminatorKind, VarBindingForm, VarDebugInfoContents,
+    Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement, StatementKind,
+    Terminator, TerminatorKind, VarBindingForm, VarDebugInfoContents,
 };
 use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{
@@ -39,7 +37,9 @@ use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
+use rustc_trait_selection::traits::{
+    Obligation, ObligationCause, ObligationCtxt, supertrait_def_ids,
+};
 use tracing::{debug, instrument};
 
 use super::explain_borrow::{BorrowExplanation, LaterUseKind};
@@ -145,10 +145,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 span,
                 desired_action.as_noun(),
                 partially_str,
-                self.describe_place_with_options(moved_place, DescribePlaceOpt {
-                    including_downcast: true,
-                    including_tuple_field: true,
-                }),
+                self.describe_place_with_options(
+                    moved_place,
+                    DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
+                ),
             );
 
             let reinit_spans = maybe_reinitialized_locations
@@ -181,7 +181,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
 
             let mut is_loop_move = false;
-            let mut in_pattern = false;
             let mut seen_spans = FxIndexSet::default();
 
             for move_site in &move_site_vec {
@@ -204,7 +203,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     self.suggest_ref_or_clone(
                         mpi,
                         &mut err,
-                        &mut in_pattern,
                         move_spans,
                         moved_place.as_ref(),
                         &mut has_suggest_reborrow,
@@ -256,15 +254,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             let place = &self.move_data.move_paths[mpi].place;
             let ty = place.ty(self.body, self.infcx.tcx).ty;
 
-            // If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
-            // Same for if we're in a loop, see #101119.
-            if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
-                if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
-                    // We have a `&mut` ref, we need to reborrow on each iteration (#62112).
-                    self.suggest_reborrow(&mut err, span, moved_place);
-                }
-            }
-
             if self.infcx.param_env.caller_bounds().iter().any(|c| {
                 c.as_trait_clause().is_some_and(|pred| {
                     pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id())
@@ -278,10 +267,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 self.suggest_adding_bounds(&mut err, ty, copy_did, span);
             }
 
-            let opt_name = self.describe_place_with_options(place.as_ref(), DescribePlaceOpt {
-                including_downcast: true,
-                including_tuple_field: true,
-            });
+            let opt_name = self.describe_place_with_options(
+                place.as_ref(),
+                DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
+            );
             let note_msg = match opt_name {
                 Some(name) => format!("`{name}`"),
                 None => "value".to_owned(),
@@ -330,7 +319,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         &self,
         mpi: MovePathIndex,
         err: &mut Diag<'infcx>,
-        in_pattern: &mut bool,
         move_spans: UseSpans<'tcx>,
         moved_place: PlaceRef<'tcx>,
         has_suggest_reborrow: &mut bool,
@@ -345,13 +333,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             expr: Option<&'hir hir::Expr<'hir>>,
             pat: Option<&'hir hir::Pat<'hir>>,
             parent_pat: Option<&'hir hir::Pat<'hir>>,
-            hir: rustc_middle::hir::map::Map<'hir>,
+            tcx: TyCtxt<'hir>,
         }
         impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
             type NestedFilter = OnlyBodies;
 
-            fn nested_visit_map(&mut self) -> Self::Map {
-                self.hir
+            fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+                self.tcx
             }
 
             fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
@@ -383,8 +371,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 hir::intravisit::walk_pat(self, p);
             }
         }
-        let hir = self.infcx.tcx.hir();
-        if let Some(body) = hir.maybe_body_owned_by(self.mir_def_id()) {
+        let tcx = self.infcx.tcx;
+        if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) {
             let expr = body.value;
             let place = &self.move_data.move_paths[mpi].place;
             let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span);
@@ -393,13 +381,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 expr: None,
                 pat: None,
                 parent_pat: None,
-                hir,
+                tcx,
             };
             finder.visit_expr(expr);
             if let Some(span) = span
                 && let Some(expr) = finder.expr
             {
-                for (_, expr) in hir.parent_iter(expr.hir_id) {
+                for (_, expr) in tcx.hir_parent_iter(expr.hir_id) {
                     if let hir::Node::Expr(expr) = expr {
                         if expr.span.contains(span) {
                             // If the let binding occurs within the same loop, then that
@@ -545,7 +533,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 && !move_span.is_dummy()
                 && !self.infcx.tcx.sess.source_map().is_imported(move_span)
             {
-                *in_pattern = true;
                 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];
                 if let Some(pat) = finder.parent_pat {
                     sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));
@@ -658,10 +645,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             clause.as_trait_clause().is_some_and(|tc| {
                 tc.self_ty().skip_binder().is_param(param.index)
                     && tc.polarity() == ty::PredicatePolarity::Positive
-                    && tcx
-                        .supertrait_def_ids(tc.def_id())
+                    && supertrait_def_ids(tcx, tc.def_id())
                         .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order())
-                        .any(|item| item.fn_has_self_parameter)
+                        .any(|item| item.is_method())
             })
         }) {
             return None;
@@ -763,26 +749,26 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
         let spans: Vec<_> = spans_set.into_iter().collect();
 
-        let (name, desc) = match self.describe_place_with_options(moved_place, DescribePlaceOpt {
-            including_downcast: true,
-            including_tuple_field: true,
-        }) {
+        let (name, desc) = match self.describe_place_with_options(
+            moved_place,
+            DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
+        ) {
             Some(name) => (format!("`{name}`"), format!("`{name}` ")),
             None => ("the variable".to_string(), String::new()),
         };
-        let path = match self.describe_place_with_options(used_place, DescribePlaceOpt {
-            including_downcast: true,
-            including_tuple_field: true,
-        }) {
+        let path = match self.describe_place_with_options(
+            used_place,
+            DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
+        ) {
             Some(name) => format!("`{name}`"),
             None => "value".to_string(),
         };
 
         // We use the statements were the binding was initialized, and inspect the HIR to look
         // for the branching codepaths that aren't covered, to point at them.
-        let map = self.infcx.tcx.hir();
-        let body = map.body_owned_by(self.mir_def_id());
-        let mut visitor = ConditionVisitor { tcx: self.infcx.tcx, spans, name, errors: vec![] };
+        let tcx = self.infcx.tcx;
+        let body = tcx.hir_body_owned_by(self.mir_def_id());
+        let mut visitor = ConditionVisitor { tcx, spans, name, errors: vec![] };
         visitor.visit_body(&body);
         let spans = visitor.spans;
 
@@ -967,7 +953,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let mut parent = None;
         // The top-most loop where the moved expression could be moved to a new binding.
         let mut outer_most_loop: Option<&hir::Expr<'_>> = None;
-        for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
+        for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
             let e = match node {
                 hir::Node::Expr(e) => e,
                 hir::Node::LetStmt(hir::LetStmt { els: Some(els), .. }) => {
@@ -1019,8 +1005,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
         }
         let loop_count: usize = tcx
-            .hir()
-            .parent_iter(expr.hir_id)
+            .hir_parent_iter(expr.hir_id)
             .map(|(_, node)| match node {
                 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Loop(..), .. }) => 1,
                 _ => 0,
@@ -1046,8 +1031,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 .collect::<Vec<Span>>();
             // All of the spans for the loops above the expression with the move error.
             let loop_spans: Vec<_> = tcx
-                .hir()
-                .parent_iter(expr.hir_id)
+                .hir_parent_iter(expr.hir_id)
                 .filter_map(|(_, node)| match node {
                     hir::Node::Expr(hir::Expr { span, kind: hir::ExprKind::Loop(..), .. }) => {
                         Some(*span)
@@ -1080,7 +1064,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 ] {
                     for (destination, sp) in elements {
                         if let Ok(hir_id) = destination.target_id
-                            && let hir::Node::Expr(expr) = tcx.hir().hir_node(hir_id)
+                            && let hir::Node::Expr(expr) = tcx.hir_node(hir_id)
                             && !matches!(
                                 sp.desugaring_kind(),
                                 Some(DesugaringKind::ForLoop | DesugaringKind::WhileLoop)
@@ -1332,7 +1316,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     }
 
     fn in_move_closure(&self, expr: &hir::Expr<'_>) -> bool {
-        for (_, node) in self.infcx.tcx.hir().parent_iter(expr.hir_id) {
+        for (_, node) in self.infcx.tcx.hir_parent_iter(expr.hir_id) {
             if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node
                 && let hir::CaptureBy::Value { .. } = closure.capture_clause
             {
@@ -1367,7 +1351,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
         // Try to find predicates on *generic params* that would allow copying `ty`
         let mut suggestion =
-            if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+            if let Some(symbol) = tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
                 format!(": {symbol}.clone()")
             } else {
                 ".clone()".to_owned()
@@ -1435,7 +1419,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let Some(hir_generics) = tcx
             .typeck_root_def_id(self.mir_def_id().to_def_id())
             .as_local()
-            .and_then(|def_id| tcx.hir().get_generics(def_id))
+            .and_then(|def_id| tcx.hir_get_generics(def_id))
         else {
             return;
         };
@@ -1887,7 +1871,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
     fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'infcx>, place: Place<'tcx>) {
         let tcx = self.infcx.tcx;
-        let hir = tcx.hir();
         let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
 
         struct FindUselessClone<'tcx> {
@@ -1915,7 +1898,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         let mut expr_finder = FindUselessClone::new(tcx, self.mir_def_id());
 
-        let body = hir.body(body_id).value;
+        let body = tcx.hir_body(body_id).value;
         expr_finder.visit_expr(body);
 
         struct Holds<'tcx> {
@@ -2104,7 +2087,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let tcx = self.infcx.tcx;
         let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?;
         let mut expr_finder = FindExprBySpan::new(span, tcx);
-        expr_finder.visit_expr(tcx.hir().body(body_id).value);
+        expr_finder.visit_expr(tcx.hir_body(body_id).value);
         expr_finder.result
     }
 
@@ -2117,7 +2100,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         issued_span: Span,
     ) {
         let tcx = self.infcx.tcx;
-        let hir = tcx.hir();
 
         let has_split_at_mut = |ty: Ty<'tcx>| {
             let ty = ty.peel_refs();
@@ -2170,7 +2152,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 return;
             };
 
-            let Some(object) = hir.parent_id_iter(index1.hir_id).find_map(|id| {
+            let Some(object) = tcx.hir_parent_id_iter(index1.hir_id).find_map(|id| {
                 if let hir::Node::Expr(expr) = tcx.hir_node(id)
                     && let hir::ExprKind::Index(obj, ..) = expr.kind
                 {
@@ -2188,7 +2170,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 return;
             };
 
-            let Some(swap_call) = hir.parent_id_iter(object.hir_id).find_map(|id| {
+            let Some(swap_call) = tcx.hir_parent_id_iter(object.hir_id).find_map(|id| {
                 if let hir::Node::Expr(call) = tcx.hir_node(id)
                     && let hir::ExprKind::Call(callee, ..) = call.kind
                     && let hir::ExprKind::Path(qpath) = callee.kind
@@ -2256,7 +2238,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     ) {
         let issue_span = issued_spans.args_or_use();
         let tcx = self.infcx.tcx;
-        let hir = tcx.hir();
 
         let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
         let typeck_results = tcx.typeck(self.mir_def_id());
@@ -2344,7 +2325,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             pat_span: None,
             head: None,
         };
-        finder.visit_expr(hir.body(body_id).value);
+        finder.visit_expr(tcx.hir_body(body_id).value);
 
         if let Some(body_expr) = finder.body_expr
             && let Some(loop_span) = finder.loop_span
@@ -2443,7 +2424,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     ) {
         let &UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
         let tcx = self.infcx.tcx;
-        let hir = tcx.hir();
 
         // Get the type of the local that we are trying to borrow
         let local = borrowed_place.local;
@@ -2452,10 +2432,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         // Get the body the error happens in
         let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
 
-        let body_expr = hir.body(body_id).value;
+        let body_expr = tcx.hir_body(body_id).value;
 
         struct ClosureFinder<'hir> {
-            hir: rustc_middle::hir::map::Map<'hir>,
+            tcx: TyCtxt<'hir>,
             borrow_span: Span,
             res: Option<(&'hir hir::Expr<'hir>, &'hir hir::Closure<'hir>)>,
             /// The path expression with the `borrow_span` span
@@ -2464,8 +2444,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         impl<'hir> Visitor<'hir> for ClosureFinder<'hir> {
             type NestedFilter = OnlyBodies;
 
-            fn nested_visit_map(&mut self) -> Self::Map {
-                self.hir
+            fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+                self.tcx
             }
 
             fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
@@ -2491,7 +2471,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         // Find the closure that most tightly wraps `capture_kind_span`
         let mut finder =
-            ClosureFinder { hir, borrow_span: capture_kind_span, res: None, error_path: None };
+            ClosureFinder { tcx, borrow_span: capture_kind_span, res: None, error_path: None };
         finder.visit_expr(body_expr);
         let Some((closure_expr, closure)) = finder.res else { return };
 
@@ -2520,13 +2500,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         );
         let ty::Tuple(params) = tupled_params.kind() else { return };
 
-        // Find the first argument with a matching type, get its name
-        let Some((_, this_name)) =
-            params.iter().zip(hir.body_param_names(closure.body)).find(|(param_ty, name)| {
+        // Find the first argument with a matching type and get its identifier.
+        let Some(this_name) = params.iter().zip(tcx.hir_body_param_idents(closure.body)).find_map(
+            |(param_ty, ident)| {
                 // FIXME: also support deref for stuff like `Rc` arguments
-                param_ty.peel_refs() == local_ty && name != &Ident::empty()
-            })
-        else {
+                if param_ty.peel_refs() == local_ty { ident } else { None }
+            },
+        ) else {
             return;
         };
 
@@ -2556,7 +2536,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
 
             let mut finder = VariableUseFinder { local_id, spans: Vec::new() };
-            finder.visit_expr(hir.body(closure.body).value);
+            finder.visit_expr(tcx.hir_body(closure.body).value);
 
             spans = finder.spans;
         } else {
@@ -2622,7 +2602,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } =
                     local.pat
                     && let Some(init) = local.init
-                    && let hir::Expr {
+                    && let &hir::Expr {
                         kind:
                             hir::ExprKind::Closure(&hir::Closure {
                                 kind: hir::ClosureKind::Closure,
@@ -3110,12 +3090,24 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             drop_span, borrow_span
         );
 
+        // `TerminatorKind::Return`'s span (the `drop_span` here) `lo` can be subtly wrong and point
+        // at a single character after the end of the function. This is somehow relied upon in
+        // existing diagnostics, and changing this in `rustc_mir_build` makes diagnostics worse in
+        // general. We fix these here.
+        let sm = self.infcx.tcx.sess.source_map();
+        let end_of_function = if drop_span.is_empty()
+            && let Ok(adjusted_span) = sm.span_extend_prev_while(drop_span, |c| c == '}')
+        {
+            adjusted_span
+        } else {
+            drop_span
+        };
         self.thread_local_value_does_not_live_long_enough(borrow_span)
             .with_span_label(
                 borrow_span,
                 "thread-local variables cannot be borrowed beyond the end of the function",
             )
-            .with_span_label(drop_span, "end of enclosing function is here")
+            .with_span_label(end_of_function, "end of enclosing function is here")
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -3197,7 +3189,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 if let Some(scope) = self.body.source_scopes.get(source_info.scope)
                     && let ClearCrossCrate::Set(scope_data) = &scope.local_data
                     && let Some(id) = self.infcx.tcx.hir_node(scope_data.lint_root).body_id()
-                    && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind
+                    && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir_body(id).value.kind
                 {
                     for stmt in block.stmts {
                         let mut visitor = NestedStatementVisitor {
@@ -3384,10 +3376,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
             Ok(string) => {
-                let coro_prefix = if string.starts_with("async") {
-                    // `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize`
-                    // to `u32`.
-                    Some(5)
+                let coro_prefix = if let Some(sub) = string.strip_prefix("async") {
+                    let trimmed_sub = sub.trim_end();
+                    if trimmed_sub.ends_with("gen") {
+                        // `async` is 5 chars long.
+                        Some((trimmed_sub.len() + 5) as _)
+                    } else {
+                        // `async` is 5 chars long.
+                        Some(5)
+                    }
                 } else if string.starts_with("gen") {
                     // `gen` is 3 chars long
                     Some(3)
@@ -3523,10 +3520,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         location: Location,
         mpi: MovePathIndex,
     ) -> (Vec<MoveSite>, Vec<Location>) {
-        fn predecessor_locations<'a, 'tcx>(
-            body: &'a mir::Body<'tcx>,
+        fn predecessor_locations<'tcx>(
+            body: &mir::Body<'tcx>,
             location: Location,
-        ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a {
+        ) -> impl Iterator<Item = Location> {
             if location.statement_index == 0 {
                 let predecessors = body.basic_blocks.predecessors()[location.block].to_vec();
                 Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb)))
@@ -3769,7 +3766,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let tcx = self.infcx.tcx;
         if let Some(Terminator { kind: TerminatorKind::Call { call_source, fn_span, .. }, .. }) =
             &self.body[loan.reserve_location.block].terminator
-            && let Some((method_did, method_args)) = rustc_middle::util::find_self_call(
+            && let Some((method_did, method_args)) = mir::find_self_call(
                 tcx,
                 self.body,
                 loan.assigned_place.local,
@@ -3782,7 +3779,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 method_args,
                 *fn_span,
                 call_source.from_hir_call(),
-                Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
+                self.infcx.tcx.fn_arg_idents(method_did)[0],
             )
         {
             err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`"));
@@ -3907,7 +3904,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         ProjectionElem::ConstantIndex { .. }
                         | ProjectionElem::Subslice { .. }
                         | ProjectionElem::Subtype(_)
-                        | ProjectionElem::Index(_) => kind,
+                        | ProjectionElem::Index(_)
+                        | ProjectionElem::UnwrapUnsafeBinder(_) => kind,
                     },
                     place_ty.projection_ty(tcx, elem),
                 )
@@ -4165,7 +4163,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
         let is_closure = self.infcx.tcx.is_closure_like(did.to_def_id());
         let fn_hir_id = self.infcx.tcx.local_def_id_to_hir_id(did);
-        let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(fn_hir_id)?;
+        let fn_decl = self.infcx.tcx.hir_fn_decl_by_hir_id(fn_hir_id)?;
 
         // We need to work out which arguments to highlight. We do this by looking
         // at the return type, where there are three cases:
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 2656e0bb6a45..f77dda0d386a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -75,10 +75,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
 
         if let Some(span) = borrow_span {
             let def_id = body.source.def_id();
-            if let Some(node) = tcx.hir().get_if_local(def_id)
+            if let Some(node) = tcx.hir_get_if_local(def_id)
                 && let Some(body_id) = node.body_id()
             {
-                let body = tcx.hir().body(body_id);
+                let body = tcx.hir_body(body_id);
                 let mut expr_finder = FindExprBySpan::new(span, tcx);
                 expr_finder.visit_expr(body.value);
                 if let Some(mut expr) = expr_finder.result {
@@ -117,7 +117,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
                 let local_decl = &body.local_decls[dropped_local];
 
                 if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
-                    && let Some((_, hir::Node::Expr(expr))) = tcx.hir().parent_iter(if_then).next()
+                    && let Some((_, hir::Node::Expr(expr))) = tcx.hir_parent_iter(if_then).next()
                     && let hir::ExprKind::If(cond, conseq, alt) = expr.kind
                     && let hir::ExprKind::Let(&hir::LetExpr {
                         span: _,
@@ -248,7 +248,98 @@ impl<'tcx> BorrowExplanation<'tcx> {
                         );
                         err.span_label(body.source_info(drop_loc).span, message);
 
-                        if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
+                        struct FindLetExpr<'hir> {
+                            span: Span,
+                            result: Option<(Span, &'hir hir::Pat<'hir>, &'hir hir::Expr<'hir>)>,
+                            tcx: TyCtxt<'hir>,
+                        }
+
+                        impl<'hir> rustc_hir::intravisit::Visitor<'hir> for FindLetExpr<'hir> {
+                            type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
+                            fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+                                self.tcx
+                            }
+                            fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+                                if let hir::ExprKind::If(cond, _conseq, _alt)
+                                | hir::ExprKind::Loop(
+                                    &hir::Block {
+                                        expr:
+                                            Some(&hir::Expr {
+                                                kind: hir::ExprKind::If(cond, _conseq, _alt),
+                                                ..
+                                            }),
+                                        ..
+                                    },
+                                    _,
+                                    hir::LoopSource::While,
+                                    _,
+                                ) = expr.kind
+                                    && let hir::ExprKind::Let(hir::LetExpr {
+                                        init: let_expr_init,
+                                        span: let_expr_span,
+                                        pat: let_expr_pat,
+                                        ..
+                                    }) = cond.kind
+                                    && let_expr_init.span.contains(self.span)
+                                {
+                                    self.result =
+                                        Some((*let_expr_span, let_expr_pat, let_expr_init))
+                                } else {
+                                    hir::intravisit::walk_expr(self, expr);
+                                }
+                            }
+                        }
+
+                        if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
+                            && let hir::Node::Expr(expr) = tcx.hir_node(if_then)
+                            && let hir::ExprKind::If(cond, conseq, alt) = expr.kind
+                            && let hir::ExprKind::Let(&hir::LetExpr {
+                                span: _,
+                                pat,
+                                init,
+                                // FIXME(#101728): enable rewrite when type ascription is
+                                // stabilized again.
+                                ty: None,
+                                recovered: _,
+                            }) = cond.kind
+                            && pat.span.can_be_used_for_suggestions()
+                            && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
+                        {
+                            suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err);
+                        } else if let Some((old, new)) = multiple_borrow_span
+                            && let def_id = body.source.def_id()
+                            && let Some(node) = tcx.hir_get_if_local(def_id)
+                            && let Some(body_id) = node.body_id()
+                            && let hir_body = tcx.hir_body(body_id)
+                            && let mut expr_finder = (FindLetExpr { span: old, result: None, tcx })
+                            && let Some((let_expr_span, let_expr_pat, let_expr_init)) = {
+                                expr_finder.visit_expr(hir_body.value);
+                                expr_finder.result
+                            }
+                            && !let_expr_span.contains(new)
+                        {
+                            // #133941: The `old` expression is at the conditional part of an
+                            // if/while let expression. Adding a semicolon won't work.
+                            // Instead, try suggesting the `matches!` macro or a temporary.
+                            if let_expr_pat
+                                .walk_short(|pat| !matches!(pat.kind, hir::PatKind::Binding(..)))
+                            {
+                                if let Ok(pat_snippet) =
+                                    tcx.sess.source_map().span_to_snippet(let_expr_pat.span)
+                                    && let Ok(init_snippet) =
+                                        tcx.sess.source_map().span_to_snippet(let_expr_init.span)
+                                {
+                                    err.span_suggestion_verbose(
+                                        let_expr_span,
+                                        "consider using the `matches!` macro",
+                                        format!("matches!({init_snippet}, {pat_snippet})"),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                } else {
+                                    err.note("consider using the `matches!` macro");
+                                }
+                            }
+                        } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() {
                             if info.tail_result_is_ignored {
                                 // #85581: If the first mutable borrow's scope contains
                                 // the second borrow, this suggestion isn't helpful.
@@ -281,23 +372,6 @@ impl<'tcx> BorrowExplanation<'tcx> {
                                     Applicability::MaybeIncorrect,
                                 );
                             };
-                        } else if let &LocalInfo::IfThenRescopeTemp { if_then } =
-                            local_decl.local_info()
-                            && let hir::Node::Expr(expr) = tcx.hir_node(if_then)
-                            && let hir::ExprKind::If(cond, conseq, alt) = expr.kind
-                            && let hir::ExprKind::Let(&hir::LetExpr {
-                                span: _,
-                                pat,
-                                init,
-                                // FIXME(#101728): enable rewrite when type ascription is
-                                // stabilized again.
-                                ty: None,
-                                recovered: _,
-                            }) = cond.kind
-                            && pat.span.can_be_used_for_suggestions()
-                            && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
-                        {
-                            suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err);
                         }
                     }
                 }
@@ -448,7 +522,7 @@ fn suggest_rewrite_if_let<G: EmissionGuarantee>(
     );
     if expr.span.can_be_used_for_suggestions() && conseq.span.can_be_used_for_suggestions() {
         let needs_block = if let Some(hir::Node::Expr(expr)) =
-            alt.and_then(|alt| tcx.hir().parent_iter(alt.hir_id).next()).map(|(_, node)| node)
+            alt.and_then(|alt| tcx.hir_parent_iter(alt.hir_id).next()).map(|(_, node)| node)
         {
             matches!(expr.kind, hir::ExprKind::If(..))
         } else {
@@ -548,8 +622,25 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             }
         }
 
+        // NLL doesn't consider boring locals for liveness, and wouldn't encounter a
+        // `Cause::LiveVar` for such a local. Polonius can't avoid computing liveness for boring
+        // locals yet, and will encounter them when trying to explain why a borrow contains a given
+        // point.
+        //
+        // We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we
+        // ensure that we don't emit live boring locals as explanations.
+        let is_local_boring = |local| {
+            if let Some(polonius_diagnostics) = self.polonius_diagnostics {
+                polonius_diagnostics.boring_nll_locals.contains(&local)
+            } else {
+                assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled());
+
+                // Boring locals are never the cause of a borrow explanation in NLLs.
+                false
+            }
+        };
         match find_use::find(body, regioncx, tcx, region_sub, use_location) {
-            Some(Cause::LiveVar(local, location)) => {
+            Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => {
                 let span = body.source_info(location).span;
                 let spans = self
                     .move_spans(Place::from(local).as_ref(), location)
@@ -592,7 +683,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                 }
             }
 
-            None => {
+            Some(Cause::LiveVar(..)) | None => {
+                // Here, under NLL: no cause was found. Under polonius: no cause was found, or a
+                // boring local was found, which we ignore like NLLs do to match its diagnostics.
                 if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
                     let (category, from_closure, span, region_name, path) =
                         self.free_region_constraint_info(borrow_region_vid, region);
diff --git a/compiler/rustc_borrowck/src/diagnostics/find_use.rs b/compiler/rustc_borrowck/src/diagnostics/find_use.rs
index 26a090f5579c..96f48840468e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/find_use.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/find_use.rs
@@ -1,7 +1,7 @@
 use std::collections::VecDeque;
 
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_middle::mir::visit::{MirVisitable, PlaceContext, Visitor};
+use rustc_middle::mir::visit::{PlaceContext, Visitor};
 use rustc_middle::mir::{self, Body, Local, Location};
 use rustc_middle::ty::{RegionVid, TyCtxt};
 
@@ -45,7 +45,22 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> {
 
             let block_data = &self.body[p.block];
 
-            match self.def_use(p, block_data.visitable(p.statement_index)) {
+            let mut visitor = DefUseVisitor {
+                body: self.body,
+                tcx: self.tcx,
+                region_vid: self.region_vid,
+                def_use_result: None,
+            };
+
+            let is_statement = p.statement_index < block_data.statements.len();
+
+            if is_statement {
+                visitor.visit_statement(&block_data.statements[p.statement_index], p);
+            } else {
+                visitor.visit_terminator(block_data.terminator.as_ref().unwrap(), p);
+            }
+
+            match visitor.def_use_result {
                 Some(DefUseResult::Def) => {}
 
                 Some(DefUseResult::UseLive { local }) => {
@@ -57,7 +72,7 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> {
                 }
 
                 None => {
-                    if p.statement_index < block_data.statements.len() {
+                    if is_statement {
                         queue.push_back(p.successor_within_block());
                     } else {
                         queue.extend(
@@ -77,19 +92,6 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> {
 
         None
     }
-
-    fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> Option<DefUseResult> {
-        let mut visitor = DefUseVisitor {
-            body: self.body,
-            tcx: self.tcx,
-            region_vid: self.region_vid,
-            def_use_result: None,
-        };
-
-        thing.apply(location, &mut visitor);
-
-        visitor.def_use_result
-    }
 }
 
 struct DefUseVisitor<'a, 'tcx> {
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index bd6f77156ca9..f9f63ae92a84 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -4,20 +4,17 @@ use std::collections::BTreeMap;
 
 use rustc_abi::{FieldIdx, VariantIdx};
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan};
+use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::{self as hir, CoroutineKind, LangItem};
 use rustc_index::IndexSlice;
-use rustc_infer::infer::{
-    BoundRegionConversionTime, NllRegionVariableOrigin, RegionVariableOrigin,
-};
+use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
 use rustc_infer::traits::SelectionError;
 use rustc_middle::bug;
-use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
     AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo,
-    LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement,
-    StatementKind, Terminator, TerminatorKind,
+    LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement,
+    StatementKind, Terminator, TerminatorKind, find_self_call,
 };
 use rustc_middle::ty::print::Print;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -29,7 +26,7 @@ use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::error_reporting::traits::call_kind::{CallDesugaringKind, call_kind};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{
-    FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions,
+    FulfillmentError, FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions,
 };
 use tracing::debug;
 
@@ -304,10 +301,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     /// End-user visible description of `place` if one can be found.
     /// If the place is a temporary for instance, `None` will be returned.
     pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
-        self.describe_place_with_options(place_ref, DescribePlaceOpt {
-            including_downcast: false,
-            including_tuple_field: true,
-        })
+        self.describe_place_with_options(
+            place_ref,
+            DescribePlaceOpt { including_downcast: false, including_tuple_field: true },
+        )
     }
 
     /// End-user visible description of `place` if one can be found. If the place is a temporary
@@ -370,6 +367,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 ProjectionElem::Downcast(..) => (),
                 ProjectionElem::OpaqueCast(..) => (),
                 ProjectionElem::Subtype(..) => (),
+                ProjectionElem::UnwrapUnsafeBinder(_) => (),
                 ProjectionElem::Field(field, _ty) => {
                     // FIXME(project-rfc_2229#36): print capture precisely here.
                     if let Some(field) = self.is_upvar_field_projection(PlaceRef {
@@ -450,9 +448,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
                 }
                 ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
-                ProjectionElem::Subtype(ty) | ProjectionElem::OpaqueCast(ty) => {
-                    PlaceTy::from_ty(*ty)
-                }
+                ProjectionElem::Subtype(ty)
+                | ProjectionElem::OpaqueCast(ty)
+                | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty),
                 ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
             },
         };
@@ -505,7 +503,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     let var_id =
                         self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable();
 
-                    Some(self.infcx.tcx.hir().name(var_id).to_string())
+                    Some(self.infcx.tcx.hir_name(var_id).to_string())
                 }
                 _ => {
                     // Might need a revision when the fields in trait RFC is implemented
@@ -569,7 +567,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         // If we didn't find an overloaded deref or index, then assume it's a
         // built in deref and check the type of the base.
         let base_ty = deref_base.ty(self.body, tcx).ty;
-        if base_ty.is_unsafe_ptr() {
+        if base_ty.is_raw_ptr() {
             BorrowedContentSource::DerefRawPointer
         } else if base_ty.is_mutable_ptr() {
             BorrowedContentSource::DerefMutableRef
@@ -587,7 +585,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         // this by hooking into the pretty printer and telling it to label the
         // lifetimes without names with the value `'0`.
         if let ty::Ref(region, ..) = ty.kind() {
-            match **region {
+            match region.kind() {
                 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
                 | ty::RePlaceholder(ty::PlaceholderRegion {
                     bound: ty::BoundRegion { kind: br, .. },
@@ -607,7 +605,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, Namespace::TypeNS);
 
         let region = if let ty::Ref(region, ..) = ty.kind() {
-            match **region {
+            match region.kind() {
                 ty::ReBound(_, ty::BoundRegion { kind: br, .. })
                 | ty::RePlaceholder(ty::PlaceholderRegion {
                     bound: ty::BoundRegion { kind: br, .. },
@@ -633,9 +631,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     ) {
         let predicate_span = path.iter().find_map(|constraint| {
             let outlived = constraint.sub;
-            if let Some(origin) = self.regioncx.var_infos.get(outlived)
-                && let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_)) =
-                    origin.origin
+            if let Some(origin) = self.regioncx.definitions.get(outlived)
+                && let NllRegionVariableOrigin::Placeholder(_) = origin.origin
                 && let ConstraintCategory::Predicate(span) = constraint.category
             {
                 Some(span)
@@ -1016,12 +1013,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
         }) = &self.body[location.block].terminator
         {
-            let Some((method_did, method_args)) = rustc_middle::util::find_self_call(
-                self.infcx.tcx,
-                self.body,
-                target_temp,
-                location.block,
-            ) else {
+            let Some((method_did, method_args)) =
+                find_self_call(self.infcx.tcx, self.body, target_temp, location.block)
+            else {
                 return normal_ret;
             };
 
@@ -1032,7 +1026,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 method_args,
                 *fn_span,
                 call_source.from_hir_call(),
-                Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
+                self.infcx.tcx.fn_arg_idents(method_did)[0],
             );
 
             return FnSelfUse {
@@ -1127,9 +1121,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             def_id, target_place, places
         );
         let hir_id = self.infcx.tcx.local_def_id_to_hir_id(def_id);
-        let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
+        let expr = &self.infcx.tcx.hir_expect_expr(hir_id).kind;
         debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
-        if let hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
+        if let &hir::ExprKind::Closure(&hir::Closure { kind, fn_decl_span, .. }) = expr {
             for (captured_place, place) in
                 self.infcx.tcx.closure_captures(def_id).iter().zip(places)
             {
@@ -1222,7 +1216,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                             .tcx
                             .typeck_root_def_id(self.mir_def_id().to_def_id())
                             .as_local()
-                            .and_then(|def_id| self.infcx.tcx.hir().get_generics(def_id))
+                            .and_then(|def_id| self.infcx.tcx.hir_get_generics(def_id))
                         && let spans = hir_generics
                             .predicates
                             .iter()
@@ -1436,17 +1430,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                                             error.obligation.predicate,
                                         )
                                     }
-                                    [errors @ .., last] => {
+                                    _ => {
                                         format!(
                                             "you could `clone` the value and consume it, if the \
-                                             following trait bounds could be satisfied: \
-                                             {} and `{}`",
-                                            errors
-                                                .iter()
-                                                .map(|e| format!("`{}`", e.obligation.predicate))
-                                                .collect::<Vec<_>>()
-                                                .join(", "),
-                                            last.obligation.predicate,
+                                             following trait bounds could be satisfied: {}",
+                                            listify(&errors, |e: &FulfillmentError<'tcx>| format!(
+                                                "`{}`",
+                                                e.obligation.predicate
+                                            ))
+                                            .unwrap(),
                                         )
                                     }
                                 };
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index beacbdbd3fa7..0394a42ea9c7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -7,7 +7,7 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::{self as hir, CaptureBy, ExprKind, HirId, Node};
 use rustc_middle::bug;
 use rustc_middle::mir::*;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
 use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
@@ -347,7 +347,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         // Find the closure that captured the binding.
         let mut expr_finder = FindExprBySpan::new(args_span, tcx);
         expr_finder.include_closures = true;
-        expr_finder.visit_expr(tcx.hir().body(body_id).value);
+        expr_finder.visit_expr(tcx.hir_body(body_id).value);
         let Some(closure_expr) = expr_finder.result else { return };
         let ExprKind::Closure(closure) = closure_expr.kind else { return };
         // We'll only suggest cloning the binding if it's a `move` closure.
@@ -357,7 +357,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let use_span = use_spans.var_or_use();
         let mut expr_finder = FindExprBySpan::new(use_span, tcx);
         expr_finder.include_closures = true;
-        expr_finder.visit_expr(tcx.hir().body(body_id).value);
+        expr_finder.visit_expr(tcx.hir_body(body_id).value);
         let Some(use_expr) = expr_finder.result else { return };
         let parent = tcx.parent_hir_node(use_expr.hir_id);
         if let Node::Expr(expr) = parent
@@ -388,7 +388,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         // Search for an appropriate place for the structured `.clone()` suggestion to be applied.
         // If we encounter a statement before the borrow error, we insert a statement there.
-        for (_, node) in tcx.hir().parent_iter(closure_expr.hir_id) {
+        for (_, node) in tcx.hir_parent_iter(closure_expr.hir_id) {
             if let Node::Stmt(stmt) = node {
                 let padding = tcx
                     .sess
@@ -502,7 +502,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 let upvar = &self.upvars[upvar_field.unwrap().index()];
                 let upvar_hir_id = upvar.get_root_variable();
                 let upvar_name = upvar.to_string(tcx);
-                let upvar_span = tcx.hir().span(upvar_hir_id);
+                let upvar_span = tcx.hir_span(upvar_hir_id);
 
                 let place_name = self.describe_any_place(move_place.as_ref());
 
@@ -688,7 +688,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         /// make it bind by reference instead (if possible)
         struct BindingFinder<'tcx> {
             typeck_results: &'tcx ty::TypeckResults<'tcx>,
-            hir: rustc_middle::hir::map::Map<'tcx>,
+            tcx: TyCtxt<'tcx>,
             /// Input: the span of the pattern we're finding bindings in
             pat_span: Span,
             /// Input: the spans of the bindings we're providing suggestions for
@@ -707,8 +707,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         impl<'tcx> Visitor<'tcx> for BindingFinder<'tcx> {
             type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
 
-            fn nested_visit_map(&mut self) -> Self::Map {
-                self.hir
+            fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+                self.tcx
             }
 
             fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result {
@@ -775,12 +775,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
         let Some(pat_span) = pat_span else { return };
 
-        let hir = self.infcx.tcx.hir();
-        let Some(body) = hir.maybe_body_owned_by(self.mir_def_id()) else { return };
+        let tcx = self.infcx.tcx;
+        let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return };
         let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
         let mut finder = BindingFinder {
             typeck_results,
-            hir,
+            tcx,
             pat_span,
             binding_spans,
             found_pat: false,
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index a6ca038282d9..56cc43275851 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -167,7 +167,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         | ProjectionElem::ConstantIndex { .. }
                         | ProjectionElem::OpaqueCast { .. }
                         | ProjectionElem::Subslice { .. }
-                        | ProjectionElem::Downcast(..),
+                        | ProjectionElem::Downcast(..)
+                        | ProjectionElem::UnwrapUnsafeBinder(_),
                     ],
             } => bug!("Unexpected immutable place."),
         }
@@ -403,7 +404,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                         pat.kind
                 {
                     if upvar_ident.name == kw::SelfLower {
-                        for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) {
+                        for (_, node) in self.infcx.tcx.hir_parent_iter(upvar_hir_id) {
                             if let Some(fn_decl) = node.fn_decl() {
                                 if !matches!(
                                     fn_decl.implicit_self,
@@ -647,10 +648,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     }
                 }
             }
-            let hir_map = self.infcx.tcx.hir();
             let def_id = self.body.source.def_id();
             let Some(local_def_id) = def_id.as_local() else { return };
-            let Some(body) = hir_map.maybe_body_owned_by(local_def_id) else { return };
+            let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id) else { return };
 
             let mut v = SuggestIndexOperatorAlternativeVisitor {
                 assign_span: span,
@@ -691,14 +691,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             true,
             td.is_local(),
             td.as_local().and_then(|tld| match self.infcx.tcx.hir_node_by_def_id(tld) {
-                Node::Item(hir::Item { kind: hir::ItemKind::Trait(_, _, _, _, items), .. }) => {
+                Node::Item(hir::Item {
+                    kind: hir::ItemKind::Trait(_, _, _, _, _, items), ..
+                }) => {
                     let mut f_in_trait_opt = None;
                     for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
                         let hi = fi.hir_id();
                         if !matches!(k, hir::AssocItemKind::Fn { .. }) {
                             continue;
                         }
-                        if self.infcx.tcx.hir().name(hi) != self.infcx.tcx.hir().name(my_hir) {
+                        if self.infcx.tcx.hir_name(hi) != self.infcx.tcx.hir_name(my_hir) {
                             continue;
                         }
                         f_in_trait_opt = Some(hi);
@@ -748,7 +750,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         // `fn foo(&x: &i32)` -> `fn foo(&(mut x): &i32)`
         let def_id = self.body.source.def_id();
         if let Some(local_def_id) = def_id.as_local()
-            && let Some(body) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
+            && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
             && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value()
             && let node = self.infcx.tcx.hir_node(hir_id)
             && let hir::Node::LetStmt(hir::LetStmt {
@@ -823,7 +825,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                             ) => {
                                 capture_reason = format!("mutable borrow of `{upvar}`");
                             }
-                            ty::UpvarCapture::ByValue => {
+                            ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {
                                 capture_reason = format!("possible mutation of `{upvar}`");
                             }
                             _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
@@ -855,7 +857,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         use hir::ExprKind::{AddrOf, Block, Call, MethodCall};
         use hir::{BorrowKind, Expr};
 
-        let hir_map = self.infcx.tcx.hir();
+        let tcx = self.infcx.tcx;
         struct Finder {
             span: Span,
         }
@@ -870,7 +872,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 }
             }
         }
-        if let Some(body) = hir_map.maybe_body_owned_by(self.mir_def_id())
+        if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id())
             && let Block(block, _) = body.value.kind
         {
             // `span` corresponds to the expression being iterated, find the `for`-loop desugared
@@ -883,17 +885,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     MethodCall(path_segment, _, _, span) => {
                         // We have `for _ in iter.read_only_iter()`, try to
                         // suggest `for _ in iter.mutable_iter()` instead.
-                        let opt_suggestions = self
-                            .infcx
-                            .tcx
+                        let opt_suggestions = tcx
                             .typeck(path_segment.hir_id.owner.def_id)
                             .type_dependent_def_id(expr.hir_id)
-                            .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
-                            .map(|def_id| self.infcx.tcx.associated_items(def_id))
+                            .and_then(|def_id| tcx.impl_of_method(def_id))
+                            .map(|def_id| tcx.associated_items(def_id))
                             .map(|assoc_items| {
                                 assoc_items
                                     .in_definition_order()
-                                    .map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx))
+                                    .map(|assoc_item_def| assoc_item_def.ident(tcx))
                                     .filter(|&ident| {
                                         let original_method_ident = path_segment.ident;
                                         original_method_ident != ident
@@ -935,12 +935,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     fn expected_fn_found_fn_mut_call(&self, err: &mut Diag<'_>, sp: Span, act: &str) {
         err.span_label(sp, format!("cannot {act}"));
 
-        let hir = self.infcx.tcx.hir();
+        let tcx = self.infcx.tcx;
         let closure_id = self.mir_hir_id();
-        let closure_span = self.infcx.tcx.def_span(self.mir_def_id());
-        let fn_call_id = self.infcx.tcx.parent_hir_id(closure_id);
-        let node = self.infcx.tcx.hir_node(fn_call_id);
-        let def_id = hir.enclosing_body_owner(fn_call_id);
+        let closure_span = tcx.def_span(self.mir_def_id());
+        let fn_call_id = tcx.parent_hir_id(closure_id);
+        let node = tcx.hir_node(fn_call_id);
+        let def_id = tcx.hir_enclosing_body_owner(fn_call_id);
         let mut look_at_return = true;
 
         // If the HIR node is a function or method call gets the def ID
@@ -950,7 +950,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 return None;
             };
 
-            let typeck_results = self.infcx.tcx.typeck(def_id);
+            let typeck_results = tcx.typeck(def_id);
 
             match kind {
                 hir::ExprKind::Call(expr, args) => {
@@ -969,7 +969,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
         };
 
-        // If we can detect the expression to be an function or method call where the closure was
+        // If we can detect the expression to be a function or method call where the closure was
         // an argument, we point at the function or method definition argument...
         if let Some((callee_def_id, call_span, call_args)) = get_call_details() {
             let arg_pos = call_args
@@ -979,10 +979,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 .map(|(pos, _)| pos)
                 .next();
 
-            let arg = match hir.get_if_local(callee_def_id) {
+            let arg = match tcx.hir_get_if_local(callee_def_id) {
                 Some(
                     hir::Node::Item(hir::Item {
-                        ident, kind: hir::ItemKind::Fn { sig, .. }, ..
+                        kind: hir::ItemKind::Fn { ident, sig, .. }, ..
                     })
                     | hir::Node::TraitItem(hir::TraitItem {
                         ident,
@@ -1018,12 +1018,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
         }
 
-        if look_at_return && hir.get_fn_id_for_return_block(closure_id).is_some() {
+        if look_at_return && tcx.hir_get_fn_id_for_return_block(closure_id).is_some() {
             // ...otherwise we are probably in the tail expression of the function, point at the
             // return type.
-            match self.infcx.tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) {
+            match tcx.hir_node_by_def_id(tcx.hir_get_parent_item(fn_call_id).def_id) {
                 hir::Node::Item(hir::Item {
-                    ident, kind: hir::ItemKind::Fn { sig, .. }, ..
+                    kind: hir::ItemKind::Fn { ident, sig, .. }, ..
                 })
                 | hir::Node::TraitItem(hir::TraitItem {
                     ident,
@@ -1049,9 +1049,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
     fn suggest_using_iter_mut(&self, err: &mut Diag<'_>) {
         let source = self.body.source;
-        let hir = self.infcx.tcx.hir();
         if let InstanceKind::Item(def_id) = source.instance
-            && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) = hir.get_if_local(def_id)
+            && let Some(Node::Expr(hir::Expr { hir_id, kind, .. })) =
+                self.infcx.tcx.hir_get_if_local(def_id)
             && let ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Closure, .. }) = kind
             && let Node::Expr(expr) = self.infcx.tcx.parent_hir_node(*hir_id)
         {
@@ -1140,10 +1140,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         let amp_mut_sugg = match *local_decl.local_info() {
             LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => {
-                let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span);
-                let additional =
-                    local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span)));
-                Some(AmpMutSugg { has_sugg: true, span: decl_span, suggestion, additional })
+                let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span);
+                let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span));
+                Some(AmpMutSugg { has_sugg: true, span, suggestion, additional })
             }
 
             LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
@@ -1202,10 +1201,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                                     opt_ty_info: None,
                                     ..
                                 })) => {
-                                    let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span);
+                                    let (span, sugg) =
+                                        suggest_ampmut_self(self.infcx.tcx, decl_span);
                                     Some(AmpMutSugg {
                                         has_sugg: true,
-                                        span: decl_span,
+                                        span,
                                         suggestion: sugg,
                                         additional: None,
                                     })
@@ -1273,7 +1273,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }) => {
                 let def_id = self.body.source.def_id();
                 let hir_id = if let Some(local_def_id) = def_id.as_local()
-                    && let Some(body) = self.infcx.tcx.hir().maybe_body_owned_by(local_def_id)
+                    && let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(local_def_id)
                 {
                     BindingFinder { span: err_label_span }.visit_body(&body).break_value()
                 } else {
@@ -1461,17 +1461,12 @@ fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symb
     }
 }
 
-fn suggest_ampmut_self<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
+fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) {
     match tcx.sess.source_map().span_to_snippet(span) {
-        Ok(snippet) => {
-            let lt_pos = snippet.find('\'');
-            if let Some(lt_pos) = lt_pos {
-                format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
-            } else {
-                "&mut self".to_string()
-            }
+        Ok(snippet) if snippet.ends_with("self") => {
+            (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string())
         }
-        _ => "&mut self".to_string(),
+        _ => (span, "&mut self".to_string()),
     }
 }
 
@@ -1639,8 +1634,8 @@ fn get_mut_span_in_struct_field<'tcx>(
 /// If possible, suggest replacing `ref` with `ref mut`.
 fn suggest_ref_mut(tcx: TyCtxt<'_>, span: Span) -> Option<Span> {
     let pattern_str = tcx.sess.source_map().span_to_snippet(span).ok()?;
-    if pattern_str.starts_with("ref")
-        && pattern_str["ref".len()..].starts_with(rustc_lexer::is_whitespace)
+    if let Some(rest) = pattern_str.strip_prefix("ref")
+        && rest.starts_with(rustc_lexer::is_whitespace)
     {
         let span = span.with_lo(span.lo() + BytePos(4)).shrink_to_lo();
         Some(span)
diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs
index 876b8f214b01..7192a889adcb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs
@@ -105,7 +105,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
                     if let Some(opaque_def_id) = opaque_def_id.as_local()
                         && let hir::OpaqueTyOrigin::FnReturn { parent, .. } =
-                            tcx.hir().expect_opaque_ty(opaque_def_id).origin
+                            tcx.hir_expect_opaque_ty(opaque_def_id).origin
                     {
                         if let Some(sugg) = impl_trait_overcapture_suggestion(
                             tcx,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index f0baa20648cd..4423edb06051 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -8,13 +8,15 @@ use rustc_hir::QPath::Resolved;
 use rustc_hir::WherePredicateKind::BoundPredicate;
 use rustc_hir::def::Res::Def;
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::VisitorExt;
 use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate};
 use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound};
 use rustc_middle::bug;
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::mir::{AnnotationSource, ConstraintCategory, ReturnConstraint};
-use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor};
+use rustc_middle::ty::{
+    self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitor, fold_regions,
+};
 use rustc_span::{Ident, Span, kw};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::error_reporting::infer::nice_region_error::{
@@ -147,9 +149,7 @@ pub(crate) enum RegionErrorKind<'tcx> {
 pub(crate) struct ErrorConstraintInfo<'tcx> {
     // fr: outlived_fr
     pub(super) fr: RegionVid,
-    pub(super) fr_is_local: bool,
     pub(super) outlived_fr: RegionVid,
-    pub(super) outlived_fr_is_local: bool,
 
     // Category and span for best blame constraint
     pub(super) category: ConstraintCategory<'tcx>,
@@ -185,9 +185,21 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }
     }
 
+    /// Map the regions in the type to named regions, where possible.
+    fn name_regions<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        fold_regions(tcx, ty, |region, _| match region.kind() {
+            ty::ReVar(vid) => self.to_error_region(vid).unwrap_or(region),
+            _ => region,
+        })
+    }
+
     /// Returns `true` if a closure is inferred to be an `FnMut` closure.
     fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
-        if let Some(ty::ReLateParam(late_param)) = self.to_error_region(fr).as_deref()
+        if let Some(r) = self.to_error_region(fr)
+            && let ty::ReLateParam(late_param) = r.kind()
             && let ty::LateParamRegionKind::ClosureEnv = late_param.kind
             && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty
         {
@@ -207,7 +219,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         lower_bound: RegionVid,
     ) {
         let mut suggestions = vec![];
-        let hir = self.infcx.tcx.hir();
+        let tcx = self.infcx.tcx;
 
         // find generic associated types in the given region 'lower_bound'
         let gat_id_and_generics = self
@@ -216,12 +228,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             .map(|placeholder| {
                 if let Some(id) = placeholder.bound.kind.get_id()
                     && let Some(placeholder_id) = id.as_local()
-                    && let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id)
-                    && let Some(generics_impl) = self
-                        .infcx
-                        .tcx
-                        .parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id))
-                        .generics()
+                    && let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
+                    && let Some(generics_impl) =
+                        tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
                 {
                     Some((gat_hir_id, generics_impl))
                 } else {
@@ -242,7 +251,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 };
                 if bound_generic_params
                     .iter()
-                    .rfind(|bgp| self.infcx.tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
+                    .rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
                     .is_some()
                 {
                     for bound in *bounds {
@@ -258,7 +267,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 return;
             };
             diag.span_note(*trait_span, fluent::borrowck_limitations_implies_static);
-            let Some(generics_fn) = hir.get_generics(self.body.source.def_id().expect_local())
+            let Some(generics_fn) = tcx.hir_get_generics(self.body.source.def_id().expect_local())
             else {
                 return;
             };
@@ -316,13 +325,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                     let type_test_span = type_test.span;
 
                     if let Some(lower_bound_region) = lower_bound_region {
-                        let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx);
+                        let generic_ty = self.name_regions(
+                            self.infcx.tcx,
+                            type_test.generic_kind.to_ty(self.infcx.tcx),
+                        );
                         let origin = RelateParamBound(type_test_span, generic_ty, None);
                         self.buffer_error(self.infcx.err_ctxt().construct_generic_bound_failure(
                             self.body.source.def_id().expect_local(),
                             type_test_span,
                             Some(origin),
-                            type_test.generic_kind,
+                            self.name_regions(self.infcx.tcx, type_test.generic_kind),
                             lower_bound_region,
                         ));
                     } else {
@@ -353,9 +365,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 }
 
                 RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
-                    let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty);
-                    let named_key = self.regioncx.name_regions(self.infcx.tcx, key);
-                    let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
+                    let named_ty =
+                        self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, hidden_ty);
+                    let named_key =
+                        self.regioncx.name_regions_for_member_constraint(self.infcx.tcx, key);
+                    let named_region = self
+                        .regioncx
+                        .name_regions_for_member_constraint(self.infcx.tcx, member_region);
                     let diag = unexpected_hidden_region_diagnostic(
                         self.infcx,
                         self.mir_def_id(),
@@ -468,14 +484,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             fr_is_local, outlived_fr_is_local, category
         );
 
-        let errci = ErrorConstraintInfo {
-            fr,
-            outlived_fr,
-            fr_is_local,
-            outlived_fr_is_local,
-            category,
-            span: cause.span,
-        };
+        let errci = ErrorConstraintInfo { fr, outlived_fr, category, span: cause.span };
 
         let mut diag = match (category, fr_is_local, outlived_fr_is_local) {
             (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => {
@@ -505,14 +514,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             ty::VarianceDiagInfo::Invariant { ty, param_index } => {
                 let (desc, note) = match ty.kind() {
                     ty::RawPtr(ty, mutbl) => {
-                        assert_eq!(*mutbl, rustc_hir::Mutability::Mut);
+                        assert_eq!(*mutbl, hir::Mutability::Mut);
                         (
                             format!("a mutable pointer to `{}`", ty),
                             "mutable pointers are invariant over their type parameter".to_string(),
                         )
                     }
                     ty::Ref(_, inner_ty, mutbl) => {
-                        assert_eq!(*mutbl, rustc_hir::Mutability::Mut);
+                        assert_eq!(*mutbl, hir::Mutability::Mut);
                         (
                             format!("a mutable reference to `{inner_ty}`"),
                             "mutable references are invariant over their type parameter"
@@ -620,7 +629,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
             if let Some(def_hir) = defined_hir {
                 let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
-                let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
+                let upvar_def_span = self.infcx.tcx.hir_span(def_hir);
                 let upvar_span = upvars_map.get(&def_hir).unwrap().span;
                 diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
                 diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
@@ -677,11 +686,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 && self.regioncx.universal_regions().defining_ty.is_fn_def())
             || self.regioncx.universal_regions().defining_ty.is_const()
         {
-            return self.report_general_error(&ErrorConstraintInfo {
-                fr_is_local: true,
-                outlived_fr_is_local: false,
-                ..*errci
-            });
+            return self.report_general_error(errci);
         }
 
         let mut diag =
@@ -759,15 +764,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
     /// ```
     #[allow(rustc::diagnostic_outside_of_impl)] // FIXME
     fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> {
-        let ErrorConstraintInfo {
-            fr,
-            fr_is_local,
-            outlived_fr,
-            outlived_fr_is_local,
-            span,
-            category,
-            ..
-        } = errci;
+        let ErrorConstraintInfo { fr, outlived_fr, span, category, .. } = errci;
 
         let mir_def_name = self.infcx.tcx.def_descr(self.mir_def_id().to_def_id());
 
@@ -786,19 +783,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
         outlived_fr_name.highlight_region_name(&mut diag);
 
-        let err_category = match (category, outlived_fr_is_local, fr_is_local) {
-            (ConstraintCategory::Return(_), true, _) => LifetimeReturnCategoryErr::WrongReturn {
+        let err_category = if matches!(category, ConstraintCategory::Return(_))
+            && self.regioncx.universal_regions().is_local_free_region(*outlived_fr)
+        {
+            LifetimeReturnCategoryErr::WrongReturn {
                 span: *span,
                 mir_def_name,
                 outlived_fr_name,
                 fr_name: &fr_name,
-            },
-            _ => LifetimeReturnCategoryErr::ShortReturn {
+            }
+        } else {
+            LifetimeReturnCategoryErr::ShortReturn {
                 span: *span,
                 category_desc: category.description(),
                 free_region_name: &fr_name,
                 outlived_fr_name,
-            },
+            }
         };
 
         diag.subdiagnostic(err_category);
@@ -833,7 +833,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         if let (Some(f), Some(outlived_f)) =
             (self.to_error_region(fr), self.to_error_region(outlived_fr))
         {
-            if *outlived_f != ty::ReStatic {
+            if outlived_f.kind() != ty::ReStatic {
                 return;
             }
             let suitable_region = self.infcx.tcx.is_suitable_region(self.mir_def_id(), f);
@@ -887,8 +887,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 if alias_ty.span.desugaring_kind().is_some() {
                     // Skip `async` desugaring `impl Future`.
                 }
-                if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
-                    if lt.ident.name == kw::Empty {
+                if let TyKind::TraitObject(_, lt) = alias_ty.kind {
+                    if lt.kind == hir::LifetimeKind::ImplicitObjectLifetimeDefault {
                         spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
                     } else {
                         spans_suggs.push((lt.ident.span, "'a".to_string()));
@@ -987,7 +987,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         for found_did in found_dids {
             let mut traits = vec![];
             let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
-            hir_v.visit_ty(self_ty);
+            hir_v.visit_ty_unambig(self_ty);
             debug!("trait spans found: {:?}", traits);
             for span in &traits {
                 let mut multi_span: MultiSpan = vec![*span].into();
@@ -1100,12 +1100,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         let closure_ty = Ty::new_closure(
             tcx,
             closure_def_id.to_def_id(),
-            ty::ClosureArgs::new(tcx, ty::ClosureArgsParts {
-                parent_args: args.parent_args(),
-                closure_kind_ty: args.kind_ty(),
-                tupled_upvars_ty: args.tupled_upvars_ty(),
-                closure_sig_as_fn_ptr_ty,
-            })
+            ty::ClosureArgs::new(
+                tcx,
+                ty::ClosureArgsParts {
+                    parent_args: args.parent_args(),
+                    closure_kind_ty: args.kind_ty(),
+                    tupled_upvars_ty: args.tupled_upvars_ty(),
+                    closure_sig_as_fn_ptr_ty,
+                },
+            )
             .args,
         );
 
@@ -1156,7 +1159,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         if ocx.select_all_or_error().is_empty() && count > 0 {
             diag.span_suggestion_verbose(
-                tcx.hir().body(*body).value.peel_blocks().span.shrink_to_lo(),
+                tcx.hir_body(*body).value.peel_blocks().span.shrink_to_lo(),
                 fluent::borrowck_dereference_suggestion,
                 "*".repeat(count),
                 Applicability::MachineApplicable,
@@ -1166,8 +1169,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
     #[allow(rustc::diagnostic_outside_of_impl)]
     fn suggest_move_on_borrowing_closure(&self, diag: &mut Diag<'_>) {
-        let map = self.infcx.tcx.hir();
-        let body = map.body_owned_by(self.mir_def_id());
+        let body = self.infcx.tcx.hir_body_owned_by(self.mir_def_id());
         let expr = &body.value.peel_blocks();
         let mut closure_span = None::<rustc_span::Span>;
         match expr.kind {
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 9349b46ec5b0..b08c10983bbc 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -194,8 +194,8 @@ impl Display for RegionName {
 }
 
 impl rustc_errors::IntoDiagArg for RegionName {
-    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
-        self.to_string().into_diag_arg()
+    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
+        self.to_string().into_diag_arg(path)
     }
 }
 
@@ -288,10 +288,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         let tcx = self.infcx.tcx;
 
         debug!("give_region_a_name: error_region = {:?}", error_region);
-        match *error_region {
+        match error_region.kind() {
             ty::ReEarlyParam(ebr) => ebr.has_name().then(|| {
                 let def_id = tcx.generics_of(self.mir_def_id()).region_param(ebr, tcx).def_id;
-                let span = tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP);
+                let span = tcx.hir_span_if_local(def_id).unwrap_or(DUMMY_SP);
                 RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyParamRegion(span) }
             }),
 
@@ -302,7 +302,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             ty::ReLateParam(late_param) => match late_param.kind {
                 ty::LateParamRegionKind::Named(region_def_id, name) => {
                     // Get the span to point to, even if we don't use the name.
-                    let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP);
+                    let span = tcx.hir_span_if_local(region_def_id).unwrap_or(DUMMY_SP);
                     debug!(
                         "bound region named: {:?}, is_named: {:?}",
                         name,
@@ -343,7 +343,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                         }
                     };
                     let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) =
-                        tcx.hir().expect_expr(self.mir_hir_id()).kind
+                        tcx.hir_expect_expr(self.mir_hir_id()).kind
                     else {
                         bug!("Closure is not defined by a closure expr");
                     };
@@ -424,7 +424,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         &self,
         argument_index: usize,
     ) -> Option<&hir::Ty<'tcx>> {
-        let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(self.mir_hir_id())?;
+        let fn_decl = self.infcx.tcx.hir_fn_decl_by_hir_id(self.mir_hir_id())?;
         let argument_hir_ty: &hir::Ty<'_> = fn_decl.inputs.get(argument_index)?;
         match argument_hir_ty.kind {
             // This indicates a variable with no type annotation, like
@@ -432,7 +432,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             // must highlight the variable.
             // NOTE(eddyb) this is handled in/by the sole caller
             // (`give_name_if_anonymous_region_appears_in_arguments`).
-            hir::TyKind::Infer => None,
+            hir::TyKind::Infer(()) => None,
 
             _ => Some(argument_hir_ty),
         }
@@ -615,7 +615,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                 }
 
                 (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => {
-                    search_stack.push((ty, hir_ty));
+                    search_stack.push((ty, hir_ty.as_unambig_ty()));
                 }
 
                 (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => {
@@ -671,7 +671,6 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
     #[instrument(level = "trace", skip(self))]
     fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
         let tcx = self.infcx.tcx;
-        let hir = tcx.hir();
 
         let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
         debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty);
@@ -682,7 +681,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         let mir_hir_id = self.mir_hir_id();
 
         let (return_span, mir_description, hir_ty) = match tcx.hir_node(mir_hir_id) {
-            hir::Node::Expr(hir::Expr {
+            hir::Node::Expr(&hir::Expr {
                 kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, kind, fn_decl_span, .. }),
                 ..
             }) => {
@@ -711,7 +710,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                         hir::CoroutineSource::Fn,
                     )) => {
                         let parent_item =
-                            tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                            tcx.hir_node_by_def_id(tcx.hir_get_parent_item(mir_hir_id).def_id);
                         let output = &parent_item
                             .fn_decl()
                             .expect("coroutine lowered from async fn should be in fn")
@@ -741,7 +740,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                         hir::CoroutineSource::Fn,
                     )) => {
                         let parent_item =
-                            tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                            tcx.hir_node_by_def_id(tcx.hir_get_parent_item(mir_hir_id).def_id);
                         let output = &parent_item
                             .fn_decl()
                             .expect("coroutine lowered from gen fn should be in fn")
@@ -768,7 +767,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                         hir::CoroutineSource::Fn,
                     )) => {
                         let parent_item =
-                            tcx.hir_node_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
+                            tcx.hir_node_by_def_id(tcx.hir_get_parent_item(mir_hir_id).def_id);
                         let output = &parent_item
                             .fn_decl()
                             .expect("coroutine lowered from async gen fn should be in fn")
@@ -874,7 +873,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             .name;
 
         let yield_span = match tcx.hir_node(self.mir_hir_id()) {
-            hir::Node::Expr(hir::Expr {
+            hir::Node::Expr(&hir::Expr {
                 kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
                 ..
             }) => tcx.sess.source_map().end_point(fn_decl_span),
@@ -897,7 +896,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         &self,
         fr: RegionVid,
     ) -> Option<RegionName> {
-        let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else {
+        let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else {
             return None;
         };
         if region.has_name() {
@@ -913,7 +912,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
 
         let found = tcx
             .any_free_region_meets(&tcx.type_of(region_parent).instantiate_identity(), |r| {
-                *r == ty::ReEarlyParam(region)
+                r.kind() == ty::ReEarlyParam(region)
             });
 
         Some(RegionName {
@@ -932,7 +931,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
         &self,
         fr: RegionVid,
     ) -> Option<RegionName> {
-        let ty::ReEarlyParam(region) = *self.to_error_region(fr)? else {
+        let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else {
             return None;
         };
         if region.has_name() {
@@ -1008,7 +1007,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                             if data.projection_term.self_ty() == ty => {}
                         _ => return false,
                     }
-                    tcx.any_free_region_meets(pred, |r| *r == ty::ReEarlyParam(region))
+                    tcx.any_free_region_meets(pred, |r| r.kind() == ty::ReEarlyParam(region))
                 })
             } else {
                 false
diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
index 191c0444c742..14ed6a27a7a1 100644
--- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs
@@ -69,8 +69,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let upvar_hir_id = upvars[upvar_index].get_root_variable();
         debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}");
 
-        let upvar_name = tcx.hir().name(upvar_hir_id);
-        let upvar_span = tcx.hir().span(upvar_hir_id);
+        let upvar_name = tcx.hir_name(upvar_hir_id);
+        let upvar_span = tcx.hir_span(upvar_hir_id);
         debug!(
             "get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}",
         );
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 91dc76f597ad..3b66142eb2ce 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -8,12 +8,12 @@
 #![feature(file_buffered)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
+#![feature(negative_impls)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
 #![feature(stmt_expr_attributes)]
 #![feature(try_blocks)]
-#![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
 use std::borrow::Cow;
@@ -21,6 +21,8 @@ use std::cell::RefCell;
 use std::marker::PhantomData;
 use std::ops::{ControlFlow, Deref};
 
+use borrow_set::LocalsStateAtExit;
+use root_cx::BorrowCheckRootCtxt;
 use rustc_abi::FieldIdx;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::graph::dominators::Dominators;
@@ -33,11 +35,11 @@ use rustc_index::{IndexSlice, IndexVec};
 use rustc_infer::infer::{
     InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
 };
-use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::*;
 use rustc_middle::query::Providers;
-use rustc_middle::ty::fold::fold_regions;
-use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode};
+use rustc_middle::ty::{
+    self, ParamEnv, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypingMode, fold_regions,
+};
 use rustc_middle::{bug, span_bug};
 use rustc_mir_dataflow::impls::{
     EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
@@ -47,7 +49,7 @@ use rustc_mir_dataflow::move_paths::{
 };
 use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
 use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
-use rustc_span::{Span, Symbol};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
 use smallvec::SmallVec;
 use tracing::{debug, instrument};
 
@@ -60,6 +62,7 @@ use crate::diagnostics::{
 use crate::path_utils::*;
 use crate::place_ext::PlaceExt;
 use crate::places_conflict::{PlaceConflictBias, places_conflict};
+use crate::polonius::PoloniusDiagnosticsContext;
 use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput};
 use crate::prefixes::PrefixSet;
 use crate::region_infer::RegionInferenceContext;
@@ -81,6 +84,7 @@ mod polonius;
 mod prefixes;
 mod region_infer;
 mod renumber;
+mod root_cx;
 mod session_diagnostics;
 mod type_check;
 mod universal_regions;
@@ -102,66 +106,202 @@ pub fn provide(providers: &mut Providers) {
     *providers = Providers { mir_borrowck, ..*providers };
 }
 
-fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
-    let (input_body, promoted) = tcx.mir_promoted(def);
+/// Provider for `query mir_borrowck`. Similar to `typeck`, this must
+/// only be called for typeck roots which will then borrowck all
+/// nested bodies as well.
+fn mir_borrowck(
+    tcx: TyCtxt<'_>,
+    def: LocalDefId,
+) -> Result<&ConcreteOpaqueTypes<'_>, ErrorGuaranteed> {
+    assert!(!tcx.is_typeck_child(def.to_def_id()));
+    let (input_body, _) = tcx.mir_promoted(def);
     debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
 
     let input_body: &Body<'_> = &input_body.borrow();
-
-    if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
-        debug!("Skipping borrowck because of injected body or tainted body");
-        // Let's make up a borrowck result! Fun times!
-        let result = BorrowCheckResult {
-            concrete_opaque_types: FxIndexMap::default(),
-            closure_requirements: None,
-            used_mut_upvars: SmallVec::new(),
-            tainted_by_errors: input_body.tainted_by_errors,
-        };
-        return tcx.arena.alloc(result);
+    if let Some(guar) = input_body.tainted_by_errors {
+        debug!("Skipping borrowck because of tainted body");
+        Err(guar)
+    } else if input_body.should_skip() {
+        debug!("Skipping borrowck because of injected body");
+        let opaque_types = ConcreteOpaqueTypes(Default::default());
+        Ok(tcx.arena.alloc(opaque_types))
+    } else {
+        let mut root_cx = BorrowCheckRootCtxt::new(tcx, def);
+        let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
+            do_mir_borrowck(&mut root_cx, def, None).0;
+        debug_assert!(closure_requirements.is_none());
+        debug_assert!(used_mut_upvars.is_empty());
+        root_cx.finalize()
     }
+}
 
-    let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
-    debug!("mir_borrowck done");
+/// Data propagated to the typeck parent by nested items.
+/// This should always be empty for the typeck root.
+#[derive(Debug)]
+struct PropagatedBorrowCheckResults<'tcx> {
+    closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
+    used_mut_upvars: SmallVec<[FieldIdx; 8]>,
+}
 
-    tcx.arena.alloc(borrowck_result)
+/// After we borrow check a closure, we are left with various
+/// requirements that we have inferred between the free regions that
+/// appear in the closure's signature or on its field types. These
+/// requirements are then verified and proved by the closure's
+/// creating function. This struct encodes those requirements.
+///
+/// The requirements are listed as being between various `RegionVid`. The 0th
+/// region refers to `'static`; subsequent region vids refer to the free
+/// regions that appear in the closure (or coroutine's) type, in order of
+/// appearance. (This numbering is actually defined by the `UniversalRegions`
+/// struct in the NLL region checker. See for example
+/// `UniversalRegions::closure_mapping`.) Note the free regions in the
+/// closure's signature and captures are erased.
+///
+/// Example: If type check produces a closure with the closure args:
+///
+/// ```text
+/// ClosureArgs = [
+///     'a,                                         // From the parent.
+///     'b,
+///     i8,                                         // the "closure kind"
+///     for<'x> fn(&'<erased> &'x u32) -> &'x u32,  // the "closure signature"
+///     &'<erased> String,                          // some upvar
+/// ]
+/// ```
+///
+/// We would "renumber" each free region to a unique vid, as follows:
+///
+/// ```text
+/// ClosureArgs = [
+///     '1,                                         // From the parent.
+///     '2,
+///     i8,                                         // the "closure kind"
+///     for<'x> fn(&'3 &'x u32) -> &'x u32,         // the "closure signature"
+///     &'4 String,                                 // some upvar
+/// ]
+/// ```
+///
+/// Now the code might impose a requirement like `'1: '2`. When an
+/// instance of the closure is created, the corresponding free regions
+/// can be extracted from its type and constrained to have the given
+/// outlives relationship.
+#[derive(Clone, Debug)]
+pub struct ClosureRegionRequirements<'tcx> {
+    /// The number of external regions defined on the closure. In our
+    /// example above, it would be 3 -- one for `'static`, then `'1`
+    /// and `'2`. This is just used for a sanity check later on, to
+    /// make sure that the number of regions we see at the callsite
+    /// matches.
+    pub num_external_vids: usize,
+
+    /// Requirements between the various free regions defined in
+    /// indices.
+    pub outlives_requirements: Vec<ClosureOutlivesRequirement<'tcx>>,
+}
+
+/// Indicates an outlives-constraint between a type or between two
+/// free regions declared on the closure.
+#[derive(Copy, Clone, Debug)]
+pub struct ClosureOutlivesRequirement<'tcx> {
+    // This region or type ...
+    pub subject: ClosureOutlivesSubject<'tcx>,
+
+    // ... must outlive this one.
+    pub outlived_free_region: ty::RegionVid,
+
+    // If not, report an error here ...
+    pub blame_span: Span,
+
+    // ... due to this reason.
+    pub category: ConstraintCategory<'tcx>,
+}
+
+// Make sure this enum doesn't unintentionally grow
+#[cfg(target_pointer_width = "64")]
+rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
+
+/// The subject of a `ClosureOutlivesRequirement` -- that is, the thing
+/// that must outlive some region.
+#[derive(Copy, Clone, Debug)]
+pub enum ClosureOutlivesSubject<'tcx> {
+    /// Subject is a type, typically a type parameter, but could also
+    /// be a projection. Indicates a requirement like `T: 'a` being
+    /// passed to the caller, where the type here is `T`.
+    Ty(ClosureOutlivesSubjectTy<'tcx>),
+
+    /// Subject is a free region from the closure. Indicates a requirement
+    /// like `'a: 'b` being passed to the caller; the region here is `'a`.
+    Region(ty::RegionVid),
+}
+
+/// Represents a `ty::Ty` for use in [`ClosureOutlivesSubject`].
+///
+/// This abstraction is necessary because the type may include `ReVar` regions,
+/// which is what we use internally within NLL code, and they can't be used in
+/// a query response.
+#[derive(Copy, Clone, Debug)]
+pub struct ClosureOutlivesSubjectTy<'tcx> {
+    inner: Ty<'tcx>,
+}
+// DO NOT implement `TypeVisitable` or `TypeFoldable` traits, because this
+// type is not recognized as a binder for late-bound region.
+impl<'tcx, I> !TypeVisitable<I> for ClosureOutlivesSubjectTy<'tcx> {}
+impl<'tcx, I> !TypeFoldable<I> for ClosureOutlivesSubjectTy<'tcx> {}
+
+impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
+    /// All regions of `ty` must be of kind `ReVar` and must represent
+    /// universal regions *external* to the closure.
+    pub fn bind(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self {
+        let inner = fold_regions(tcx, ty, |r, depth| match r.kind() {
+            ty::ReVar(vid) => {
+                let br = ty::BoundRegion {
+                    var: ty::BoundVar::from_usize(vid.index()),
+                    kind: ty::BoundRegionKind::Anon,
+                };
+                ty::Region::new_bound(tcx, depth, br)
+            }
+            _ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"),
+        });
+
+        Self { inner }
+    }
+
+    pub fn instantiate(
+        self,
+        tcx: TyCtxt<'tcx>,
+        mut map: impl FnMut(ty::RegionVid) -> ty::Region<'tcx>,
+    ) -> Ty<'tcx> {
+        fold_regions(tcx, self.inner, |r, depth| match r.kind() {
+            ty::ReBound(debruijn, br) => {
+                debug_assert_eq!(debruijn, depth);
+                map(ty::RegionVid::from_usize(br.var.index()))
+            }
+            _ => bug!("unexpected region {r:?}"),
+        })
+    }
 }
 
 /// Perform the actual borrow checking.
 ///
 /// Use `consumer_options: None` for the default behavior of returning
-/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
-/// to the given [`ConsumerOptions`].
-#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
+/// [`PropagatedBorrowCheckResults`] only. Otherwise, return [`BodyWithBorrowckFacts`]
+/// according to the given [`ConsumerOptions`].
+///
+/// For nested bodies this should only be called through `root_cx.get_or_insert_nested`.
+#[instrument(skip(root_cx), level = "debug")]
 fn do_mir_borrowck<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    input_body: &Body<'tcx>,
-    input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
+    def: LocalDefId,
     consumer_options: Option<ConsumerOptions>,
-) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
-    let def = input_body.source.def_id().expect_local();
+) -> (PropagatedBorrowCheckResults<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
+    let tcx = root_cx.tcx;
     let infcx = BorrowckInferCtxt::new(tcx, def);
+    let (input_body, promoted) = tcx.mir_promoted(def);
+    let input_body: &Body<'_> = &input_body.borrow();
+    let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
     if let Some(e) = input_body.tainted_by_errors {
         infcx.set_tainted_by_errors(e);
-    }
-
-    let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
-    for var_debug_info in &input_body.var_debug_info {
-        if let VarDebugInfoContents::Place(place) = var_debug_info.value {
-            if let Some(local) = place.as_local() {
-                if let Some(prev_name) = local_names[local]
-                    && var_debug_info.name != prev_name
-                {
-                    span_bug!(
-                        var_debug_info.source_info.span,
-                        "local {:?} has many names (`{}` vs `{}`)",
-                        local,
-                        prev_name,
-                        var_debug_info.name
-                    );
-                }
-                local_names[local] = Some(var_debug_info.name);
-            }
-        }
+        root_cx.set_tainted_by_errors(e);
     }
 
     // Replace all regions with fresh inference variables. This
@@ -170,15 +310,9 @@ fn do_mir_borrowck<'tcx>(
     // will have a lifetime tied to the inference context.
     let mut body_owned = input_body.clone();
     let mut promoted = input_promoted.to_owned();
-    let free_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted);
+    let universal_regions = nll::replace_regions_in_mir(&infcx, &mut body_owned, &mut promoted);
     let body = &body_owned; // no further changes
 
-    // FIXME(-Znext-solver): A bit dubious that we're only registering
-    // predefined opaques in the typeck root.
-    if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
-        infcx.register_predefined_opaques_for_next_solver(def);
-    }
-
     let location_table = PoloniusLocationTable::new(body);
 
     let move_data = MoveData::gather_moves(body, tcx, |_| true);
@@ -187,21 +321,21 @@ fn do_mir_borrowck<'tcx>(
         .iterate_to_fixpoint(tcx, body, Some("borrowck"))
         .into_results_cursor(body);
 
-    let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(def).is_fn_or_closure();
+    let locals_are_invalidated_at_exit = tcx.hir_body_owner_kind(def).is_fn_or_closure();
     let borrow_set = BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &move_data);
 
     // Compute non-lexical lifetimes.
     let nll::NllOutput {
         regioncx,
-        opaque_type_values,
         polonius_input,
         polonius_output,
         opt_closure_req,
         nll_errors,
-        localized_outlives_constraints,
+        polonius_diagnostics,
     } = nll::compute_regions(
+        root_cx,
         &infcx,
-        free_regions,
+        universal_regions,
         body,
         &promoted,
         &location_table,
@@ -214,31 +348,23 @@ fn do_mir_borrowck<'tcx>(
     // Dump MIR results into a file, if that is enabled. This lets us
     // write unit-tests, as well as helping with debugging.
     nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);
-
-    // We also have a `#[rustc_regions]` annotation that causes us to dump
-    // information.
-    let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
-    nll::dump_annotation(
+    polonius::dump_polonius_mir(
         &infcx,
         body,
         &regioncx,
         &opt_closure_req,
-        &opaque_type_values,
-        diags_buffer,
+        &borrow_set,
+        polonius_diagnostics.as_ref(),
     );
 
-    let movable_coroutine =
-        // The first argument is the coroutine type passed by value
-        if let Some(local) = body.local_decls.raw.get(1)
-        // Get the interior types and args which typeck computed
-        && let ty::Coroutine(def_id, _) = *local.ty.kind()
-        && tcx.coroutine_movability(def_id) == hir::Movability::Movable
-    {
-        true
-    } else {
-        false
-    };
+    // We also have a `#[rustc_regions]` annotation that causes us to dump
+    // information.
+    nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req);
+
+    let movable_coroutine = body.coroutine.is_some()
+        && tcx.coroutine_movability(def.to_def_id()) == hir::Movability::Movable;
 
+    let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
     // While promoteds should mostly be correct by construction, we need to check them for
     // invalid moves to detect moving out of arrays:`struct S; fn main() { &([S][0]); }`.
     for promoted_body in &promoted {
@@ -248,6 +374,7 @@ fn do_mir_borrowck<'tcx>(
         // this check out of `MirBorrowckCtxt`, actually doing so is far from trivial.
         let move_data = MoveData::gather_moves(promoted_body, tcx, |_| true);
         let mut promoted_mbcx = MirBorrowckCtxt {
+            root_cx,
             infcx: &infcx,
             body: promoted_body,
             move_data: &move_data,
@@ -255,7 +382,6 @@ fn do_mir_borrowck<'tcx>(
             location_table: &location_table,
             movable_coroutine,
             fn_self_span_reported: Default::default(),
-            locals_are_invalidated_at_exit,
             access_place_error_reported: Default::default(),
             reservation_error_reported: Default::default(),
             uninitialized_error_reported: Default::default(),
@@ -270,6 +396,7 @@ fn do_mir_borrowck<'tcx>(
             polonius_output: None,
             move_errors: Vec::new(),
             diags_buffer,
+            polonius_diagnostics: polonius_diagnostics.as_ref(),
         };
         struct MoveVisitor<'a, 'b, 'infcx, 'tcx> {
             ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
@@ -286,13 +413,33 @@ fn do_mir_borrowck<'tcx>(
         promoted_mbcx.report_move_errors();
     }
 
+    let mut local_names = IndexVec::from_elem(None, &body.local_decls);
+    for var_debug_info in &body.var_debug_info {
+        if let VarDebugInfoContents::Place(place) = var_debug_info.value {
+            if let Some(local) = place.as_local() {
+                if let Some(prev_name) = local_names[local]
+                    && var_debug_info.name != prev_name
+                {
+                    span_bug!(
+                        var_debug_info.source_info.span,
+                        "local {:?} has many names (`{}` vs `{}`)",
+                        local,
+                        prev_name,
+                        var_debug_info.name
+                    );
+                }
+                local_names[local] = Some(var_debug_info.name);
+            }
+        }
+    }
+
     let mut mbcx = MirBorrowckCtxt {
+        root_cx,
         infcx: &infcx,
         body,
         move_data: &move_data,
         location_table: &location_table,
         movable_coroutine,
-        locals_are_invalidated_at_exit,
         fn_self_span_reported: Default::default(),
         access_place_error_reported: Default::default(),
         reservation_error_reported: Default::default(),
@@ -305,9 +452,10 @@ fn do_mir_borrowck<'tcx>(
         local_names,
         region_names: RefCell::default(),
         next_region_name: RefCell::new(1),
-        polonius_output,
         move_errors: Vec::new(),
         diags_buffer,
+        polonius_output: polonius_output.as_deref(),
+        polonius_diagnostics: polonius_diagnostics.as_ref(),
     };
 
     // Compute and report region errors, if any.
@@ -323,16 +471,6 @@ fn do_mir_borrowck<'tcx>(
 
     mbcx.report_move_errors();
 
-    // If requested, dump polonius MIR.
-    polonius::dump_polonius_mir(
-        &infcx,
-        body,
-        &regioncx,
-        &borrow_set,
-        localized_outlives_constraints,
-        &opt_closure_req,
-    );
-
     // For each non-user used mutable variable, check if it's been assigned from
     // a user-declared local. If so, then put that local into the used_mut set.
     // Note that this set is expected to be small - only upvars from closures
@@ -353,17 +491,16 @@ fn do_mir_borrowck<'tcx>(
 
     debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
     mbcx.lint_unused_mut();
-    let tainted_by_errors = mbcx.emit_errors();
+    if let Some(guar) = mbcx.emit_errors() {
+        mbcx.root_cx.set_tainted_by_errors(guar);
+    }
 
-    let result = BorrowCheckResult {
-        concrete_opaque_types: opaque_type_values,
+    let result = PropagatedBorrowCheckResults {
         closure_requirements: opt_closure_req,
         used_mut_upvars: mbcx.used_mut_upvars,
-        tainted_by_errors,
     };
 
     let body_with_facts = if consumer_options.is_some() {
-        let output_facts = mbcx.polonius_output;
         Some(Box::new(BodyWithBorrowckFacts {
             body: body_owned,
             promoted,
@@ -371,7 +508,7 @@ fn do_mir_borrowck<'tcx>(
             region_inference_context: regioncx,
             location_table: polonius_input.as_ref().map(|_| location_table),
             input_facts: polonius_input,
-            output_facts,
+            output_facts: polonius_output,
         }))
     } else {
         None
@@ -431,7 +568,12 @@ pub(crate) struct BorrowckInferCtxt<'tcx> {
 
 impl<'tcx> BorrowckInferCtxt<'tcx> {
     pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
-        let infcx = tcx.infer_ctxt().build(TypingMode::analysis_in_body(tcx, def_id));
+        let typing_mode = if tcx.use_typing_mode_borrowck() {
+            TypingMode::borrowck(tcx, def_id)
+        } else {
+            TypingMode::analysis_in_body(tcx, def_id)
+        };
+        let infcx = tcx.infer_ctxt().build(typing_mode);
         let param_env = tcx.param_env(def_id);
         BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()), param_env }
     }
@@ -478,28 +620,6 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
 
         next_region
     }
-
-    /// With the new solver we prepopulate the opaque type storage during
-    /// MIR borrowck with the hidden types from HIR typeck. This is necessary
-    /// to avoid ambiguities as earlier goals can rely on the hidden type
-    /// of an opaque which is only constrained by a later goal.
-    fn register_predefined_opaques_for_next_solver(&self, def_id: LocalDefId) {
-        let tcx = self.tcx;
-        // OK to use the identity arguments for each opaque type key, since
-        // we remap opaques from HIR typeck back to their definition params.
-        for data in tcx.typeck(def_id).concrete_opaque_types.iter().map(|(k, v)| (*k, *v)) {
-            // HIR typeck did not infer the regions of the opaque, so we instantiate
-            // them with fresh inference variables.
-            let (key, hidden_ty) = fold_regions(tcx, data, |_, _| {
-                self.next_nll_region_var_in_universe(
-                    NllRegionVariableOrigin::Existential { from_forall: false },
-                    ty::UniverseIndex::ROOT,
-                )
-            });
-
-            self.inject_new_hidden_type_unchecked(key, hidden_ty);
-        }
-    }
 }
 
 impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
@@ -511,6 +631,7 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
 }
 
 struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
+    root_cx: &'a mut BorrowCheckRootCtxt<'tcx>,
     infcx: &'infcx BorrowckInferCtxt<'tcx>,
     body: &'a Body<'tcx>,
     move_data: &'a MoveData<'tcx>,
@@ -520,13 +641,6 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
     location_table: &'a PoloniusLocationTable,
 
     movable_coroutine: bool,
-    /// This keeps track of whether local variables are free-ed when the function
-    /// exits even without a `StorageDead`, which appears to be the case for
-    /// constants.
-    ///
-    /// I'm not sure this is the right approach - @eddyb could you try and
-    /// figure this out?
-    locals_are_invalidated_at_exit: bool,
     /// This field keeps track of when borrow errors are reported in the access_place function
     /// so that there is no duplicate reporting. This field cannot also be used for the conflicting
     /// borrow errors that is handled by the `reservation_error_reported` field as the inclusion
@@ -574,11 +688,13 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
     /// The counter for generating new region names.
     next_region_name: RefCell<usize>,
 
-    /// Results of Polonius analysis.
-    polonius_output: Option<Box<PoloniusOutput>>,
-
     diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
     move_errors: Vec<MoveError<'tcx>>,
+
+    /// Results of Polonius analysis.
+    polonius_output: Option<&'a PoloniusOutput>,
+    /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
+    polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>,
 }
 
 // Check that:
@@ -643,7 +759,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
             | StatementKind::StorageLive(..) => {}
             // This does not affect borrowck
             StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => {
-                self.check_backward_incompatible_drop(location, (**place, span), state);
+                self.check_backward_incompatible_drop(location, **place, state);
             }
             StatementKind::StorageDead(local) => {
                 self.access_place(
@@ -800,13 +916,20 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<
             | TerminatorKind::Return
             | TerminatorKind::TailCall { .. }
             | TerminatorKind::CoroutineDrop => {
-                // Returning from the function implicitly kills storage for all locals and statics.
-                // Often, the storage will already have been killed by an explicit
-                // StorageDead, but we don't always emit those (notably on unwind paths),
-                // so this "extra check" serves as a kind of backup.
-                for i in state.borrows.iter() {
-                    let borrow = &self.borrow_set[i];
-                    self.check_for_invalidation_at_exit(loc, borrow, span);
+                match self.borrow_set.locals_state_at_exit() {
+                    LocalsStateAtExit::AllAreInvalidated => {
+                        // Returning from the function implicitly kills storage for all locals and statics.
+                        // Often, the storage will already have been killed by an explicit
+                        // StorageDead, but we don't always emit those (notably on unwind paths),
+                        // so this "extra check" serves as a kind of backup.
+                        for i in state.borrows.iter() {
+                            let borrow = &self.borrow_set[i];
+                            self.check_for_invalidation_at_exit(loc, borrow, span);
+                        }
+                    }
+                    // If we do not implicitly invalidate all locals on exit,
+                    // we check for conflicts when dropping or moving this local.
+                    LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved: _ } => {}
                 }
             }
 
@@ -1163,13 +1286,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
         error_reported
     }
 
-    /// Through #123739, backward incompatible drops (BIDs) are introduced.
+    /// Through #123739, `BackwardIncompatibleDropHint`s (BIDs) are introduced.
     /// We would like to emit lints whether borrow checking fails at these future drop locations.
     #[instrument(level = "debug", skip(self, state))]
     fn check_backward_incompatible_drop(
         &mut self,
         location: Location,
-        (place, place_span): (Place<'tcx>, Span),
+        place: Place<'tcx>,
         state: &BorrowckDomain,
     ) {
         let tcx = self.infcx.tcx;
@@ -1284,15 +1407,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                 );
             }
 
-            &Rvalue::RawPtr(mutability, place) => {
-                let access_kind = match mutability {
-                    Mutability::Mut => (
+            &Rvalue::RawPtr(kind, place) => {
+                let access_kind = match kind {
+                    RawPtrKind::Mut => (
                         Deep,
                         Write(WriteKind::MutableBorrow(BorrowKind::Mut {
                             kind: MutBorrowKind::Default,
                         })),
                     ),
-                    Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
+                    RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
+                    RawPtrKind::FakeForPtrMetadata => {
+                        (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy))
+                    }
                 };
 
                 self.access_place(
@@ -1378,11 +1504,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                     | AggregateKind::CoroutineClosure(def_id, _)
                     | AggregateKind::Coroutine(def_id, _) => {
                         let def_id = def_id.expect_local();
-                        let BorrowCheckResult { used_mut_upvars, .. } =
-                            self.infcx.tcx.mir_borrowck(def_id);
+                        let used_mut_upvars = self.root_cx.used_mut_upvars(def_id);
                         debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
-                        for field in used_mut_upvars {
-                            self.propagate_closure_used_mut_upvar(&operands[*field]);
+                        // FIXME: We're cloning the `SmallVec` here to avoid borrowing `root_cx`
+                        // when calling `propagate_closure_used_mut_upvar`. This should ideally
+                        // be unnecessary.
+                        for field in used_mut_upvars.clone() {
+                            self.propagate_closure_used_mut_upvar(&operands[field]);
                         }
                     }
                     AggregateKind::Adt(..)
@@ -1395,6 +1523,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                     self.consume_operand(location, (operand, span), state);
                 }
             }
+
+            Rvalue::WrapUnsafeBinder(op, _) => {
+                self.consume_operand(location, (op, span), state);
+            }
         }
     }
 
@@ -1478,14 +1610,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                         let stmt = &bbd.statements[loc.statement_index];
                         debug!("temporary assigned in: stmt={:?}", stmt);
 
-                        if let StatementKind::Assign(box (_, Rvalue::Ref(_, _, source))) = stmt.kind
-                        {
-                            propagate_closure_used_mut_place(self, source);
-                        } else {
-                            bug!(
-                                "closures should only capture user variables \
+                        match stmt.kind {
+                            StatementKind::Assign(box (
+                                _,
+                                Rvalue::Ref(_, _, source)
+                                | Rvalue::Use(Operand::Copy(source) | Operand::Move(source)),
+                            )) => {
+                                propagate_closure_used_mut_place(self, source);
+                            }
+                            _ => {
+                                bug!(
+                                    "closures should only capture user variables \
                                  or references to user variables"
-                            );
+                                );
+                            }
                         }
                     }
                     _ => propagate_closure_used_mut_place(self, place),
@@ -1563,22 +1701,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
         // we'll have a memory leak) and assume that all statics have a destructor.
         //
         // FIXME: allow thread-locals to borrow other thread locals?
-
-        let (might_be_alive, will_be_dropped) =
-            if self.body.local_decls[root_place.local].is_ref_to_thread_local() {
-                // Thread-locals might be dropped after the function exits
-                // We have to dereference the outer reference because
-                // borrows don't conflict behind shared references.
-                root_place.projection = TyCtxtConsts::DEREF_PROJECTION;
-                (true, true)
-            } else {
-                (false, self.locals_are_invalidated_at_exit)
-            };
-
-        if !will_be_dropped {
-            debug!("place_is_invalidated_at_exit({:?}) - won't be dropped", place);
-            return;
-        }
+        let might_be_alive = if self.body.local_decls[root_place.local].is_ref_to_thread_local() {
+            // Thread-locals might be dropped after the function exits
+            // We have to dereference the outer reference because
+            // borrows don't conflict behind shared references.
+            root_place.projection = TyCtxtConsts::DEREF_PROJECTION;
+            true
+        } else {
+            false
+        };
 
         let sd = if might_be_alive { Deep } else { Shallow(None) };
 
@@ -1655,9 +1786,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
             match elem {
                 ProjectionElem::Deref => match place_ty.ty.kind() {
                     ty::Ref(..) | ty::RawPtr(..) => {
-                        self.move_errors.push(MoveError::new(place, location, BorrowedContent {
-                            target_place: place_ref.project_deeper(&[elem], tcx),
-                        }));
+                        self.move_errors.push(MoveError::new(
+                            place,
+                            location,
+                            BorrowedContent {
+                                target_place: place_ref.project_deeper(&[elem], tcx),
+                            },
+                        ));
                         return;
                     }
                     ty::Adt(adt, _) => {
@@ -1767,7 +1902,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                 // So it's safe to skip these.
                 ProjectionElem::OpaqueCast(_)
                 | ProjectionElem::Subtype(_)
-                | ProjectionElem::Downcast(_, _) => (),
+                | ProjectionElem::Downcast(_, _)
+                | ProjectionElem::UnwrapUnsafeBinder(_) => (),
             }
 
             place_ty = place_ty.projection_ty(tcx, elem);
@@ -2001,6 +2137,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                 // FIXME: is this true even if P is an adt with a dtor?
                 { }
 
+                ProjectionElem::UnwrapUnsafeBinder(_) => {
+                    check_parent_of_field(self, location, place_base, span, state);
+                }
+
                 // assigning to (*P) requires P to be initialized
                 ProjectionElem::Deref => {
                     self.check_if_full_path_is_moved(
@@ -2381,7 +2521,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
                     | ProjectionElem::Subslice { .. }
                     | ProjectionElem::Subtype(..)
                     | ProjectionElem::OpaqueCast { .. }
-                    | ProjectionElem::Downcast(..) => {
+                    | ProjectionElem::Downcast(..)
+                    | ProjectionElem::UnwrapUnsafeBinder(_) => {
                         let upvar_field_projection = self.is_upvar_field_projection(place);
                         if let Some(field) = upvar_field_projection {
                             let upvar = &self.upvars[field.index()];
@@ -2452,19 +2593,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
         let body = self.body;
         for local in body.mut_vars_and_args_iter().filter(|local| !self.used_mut.contains(local)) {
             let local_decl = &body.local_decls[local];
-            let lint_root = match &body.source_scopes[local_decl.source_info.scope].local_data {
-                ClearCrossCrate::Set(data) => data.lint_root,
-                _ => continue,
+            let ClearCrossCrate::Set(SourceScopeLocalData { lint_root, .. }) =
+                body.source_scopes[local_decl.source_info.scope].local_data
+            else {
+                continue;
             };
 
             // Skip over locals that begin with an underscore or have no name
-            match self.local_names[local] {
-                Some(name) => {
-                    if name.as_str().starts_with('_') {
-                        continue;
-                    }
-                }
-                None => continue,
+            if self.local_names[local].is_none_or(|name| name.as_str().starts_with('_')) {
+                continue;
             }
 
             let span = local_decl.source_info.span;
diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs
index a0adf471fd31..bdd0f6fe11e0 100644
--- a/compiler/rustc_borrowck/src/member_constraints.rs
+++ b/compiler/rustc_borrowck/src/member_constraints.rs
@@ -1,7 +1,6 @@
 use std::hash::Hash;
 use std::ops::Index;
 
-use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::ty::{self, Ty};
@@ -147,9 +146,7 @@ impl<'tcx, R> MemberConstraintSet<'tcx, R>
 where
     R: Copy + Hash + Eq,
 {
-    pub(crate) fn all_indices(
-        &self,
-    ) -> impl Iterator<Item = NllMemberConstraintIndex> + Captures<'tcx> + '_ {
+    pub(crate) fn all_indices(&self) -> impl Iterator<Item = NllMemberConstraintIndex> {
         self.constraints.indices()
     }
 
@@ -159,7 +156,7 @@ where
     pub(crate) fn indices(
         &self,
         member_region_vid: R,
-    ) -> impl Iterator<Item = NllMemberConstraintIndex> + Captures<'tcx> + '_ {
+    ) -> impl Iterator<Item = NllMemberConstraintIndex> {
         let mut next = self.first_constraints.get(&member_region_vid).cloned();
         std::iter::from_fn(move || -> Option<NllMemberConstraintIndex> {
             if let Some(current) = next {
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 35264bd1a707..fe899bb054fa 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -1,21 +1,16 @@
 //! The entry point of the NLL borrow checker.
 
+use std::io;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::str::FromStr;
-use std::{env, io};
 
 use polonius_engine::{Algorithm, Output};
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_hir::def_id::LocalDefId;
 use rustc_index::IndexSlice;
 use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
-use rustc_middle::mir::{
-    Body, ClosureOutlivesSubject, ClosureRegionRequirements, PassWhere, Promoted, create_dump_file,
-    dump_enabled, dump_mir,
-};
+use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt};
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_mir_dataflow::ResultsCursor;
 use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::MoveData;
@@ -26,28 +21,31 @@ use tracing::{debug, instrument};
 
 use crate::borrow_set::BorrowSet;
 use crate::consumers::ConsumerOptions;
-use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
-use crate::polonius::LocalizedOutlivesConstraintSet;
+use crate::diagnostics::RegionErrors;
+use crate::polonius::PoloniusDiagnosticsContext;
 use crate::polonius::legacy::{
     PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
 };
 use crate::region_infer::RegionInferenceContext;
 use crate::type_check::{self, MirTypeckResults};
 use crate::universal_regions::UniversalRegions;
-use crate::{BorrowckInferCtxt, polonius, renumber};
+use crate::{
+    BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements,
+    polonius, renumber,
+};
 
 /// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
 /// closure requirements to propagate, and any generated errors.
 pub(crate) struct NllOutput<'tcx> {
     pub regioncx: RegionInferenceContext<'tcx>,
-    pub opaque_type_values: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
     pub polonius_input: Option<Box<PoloniusFacts>>,
     pub polonius_output: Option<Box<PoloniusOutput>>,
     pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
     pub nll_errors: RegionErrors<'tcx>,
 
-    /// When using `-Zpolonius=next`: the localized typeck and liveness constraints.
-    pub localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>,
+    /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
+    /// localized typeck and liveness constraints.
+    pub polonius_diagnostics: Option<PoloniusDiagnosticsContext>,
 }
 
 /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
@@ -78,6 +76,7 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
 ///
 /// This may result in errors being reported.
 pub(crate) fn compute_regions<'a, 'tcx>(
+    root_cx: &mut BorrowCheckRootCtxt<'tcx>,
     infcx: &BorrowckInferCtxt<'tcx>,
     universal_regions: UniversalRegions<'tcx>,
     body: &Body<'tcx>,
@@ -105,6 +104,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
         opaque_type_values,
         polonius_context,
     } = type_check::type_check(
+        root_cx,
         infcx,
         body,
         promoted,
@@ -117,11 +117,6 @@ pub(crate) fn compute_regions<'a, 'tcx>(
         Rc::clone(&location_map),
     );
 
-    // Create the region inference context, taking ownership of the
-    // region inference data that was contained in `infcx`, and the
-    // base constraints generated by the type-check.
-    let var_infos = infcx.get_region_var_infos();
-
     // If requested, emit legacy polonius facts.
     polonius::legacy::emit_facts(
         &mut polonius_facts,
@@ -134,17 +129,12 @@ pub(crate) fn compute_regions<'a, 'tcx>(
         &constraints,
     );
 
-    let mut regioncx = RegionInferenceContext::new(
-        infcx,
-        var_infos,
-        constraints,
-        universal_region_relations,
-        location_map,
-    );
+    let mut regioncx =
+        RegionInferenceContext::new(infcx, constraints, universal_region_relations, location_map);
 
     // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
     // and use them to compute loan liveness.
-    let localized_outlives_constraints = polonius_context.as_ref().map(|polonius_context| {
+    let polonius_diagnostics = polonius_context.map(|polonius_context| {
         polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set)
     });
 
@@ -159,9 +149,8 @@ pub(crate) fn compute_regions<'a, 'tcx>(
         }
 
         if polonius_output {
-            let algorithm =
-                env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid"));
-            let algorithm = Algorithm::from_str(&algorithm).unwrap();
+            let algorithm = infcx.tcx.env_var("POLONIUS_ALGORITHM").unwrap_or("Hybrid");
+            let algorithm = Algorithm::from_str(algorithm).unwrap();
             debug!("compute_regions: using polonius algorithm {:?}", algorithm);
             let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis");
             Some(Box::new(Output::compute(polonius_facts, algorithm, false)))
@@ -179,16 +168,15 @@ pub(crate) fn compute_regions<'a, 'tcx>(
         infcx.set_tainted_by_errors(guar);
     }
 
-    let remapped_opaque_tys = regioncx.infer_opaque_types(infcx, opaque_type_values);
+    regioncx.infer_opaque_types(root_cx, infcx, opaque_type_values);
 
     NllOutput {
         regioncx,
-        opaque_type_values: remapped_opaque_tys,
         polonius_input: polonius_facts.map(Box::new),
         polonius_output,
         opt_closure_req: closure_region_requirements,
         nll_errors,
-        localized_outlives_constraints,
+        polonius_diagnostics,
     }
 }
 
@@ -299,8 +287,6 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
-    opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
-    diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
 ) {
     let tcx = infcx.tcx;
     let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
@@ -316,7 +302,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
     // better.
 
     let def_span = tcx.def_span(body.source.def_id());
-    let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
+    let err = if let Some(closure_region_requirements) = closure_region_requirements {
         let mut err = infcx.dcx().struct_span_note(def_span, "external requirements");
 
         regioncx.annotate(tcx, &mut err);
@@ -338,15 +324,11 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
     } else {
         let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements");
         regioncx.annotate(tcx, &mut err);
-
         err
     };
 
-    if !opaque_type_values.is_empty() {
-        err.note(format!("Inferred opaque type values:\n{opaque_type_values:#?}"));
-    }
-
-    diagnostics_buffer.buffer_non_error(err);
+    // FIXME(@lcnr): We currently don't dump the inferred hidden types here.
+    err.emit();
 }
 
 fn for_each_region_constraint<'tcx>(
diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs
index 679e111caa98..cf3e82426e8a 100644
--- a/compiler/rustc_borrowck/src/places_conflict.rs
+++ b/compiler/rustc_borrowck/src/places_conflict.rs
@@ -250,7 +250,8 @@ fn place_components_conflict<'tcx>(
                 | (ProjectionElem::Subslice { .. }, _, _)
                 | (ProjectionElem::OpaqueCast { .. }, _, _)
                 | (ProjectionElem::Subtype(_), _, _)
-                | (ProjectionElem::Downcast { .. }, _, _) => {
+                | (ProjectionElem::Downcast { .. }, _, _)
+                | (ProjectionElem::UnwrapUnsafeBinder(_), _, _) => {
                     // Recursive case. This can still be disjoint on a
                     // further iteration if this a shallow access and
                     // there's a deref later on, e.g., a borrow
@@ -519,5 +520,9 @@ fn place_projection_conflict<'tcx>(
             pi1_elem,
             pi2_elem
         ),
+
+        (ProjectionElem::UnwrapUnsafeBinder(_), _) => {
+            todo!()
+        }
     }
 }
diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs
index a6d801490344..6a943e192082 100644
--- a/compiler/rustc_borrowck/src/polonius/dump.rs
+++ b/compiler/rustc_borrowck/src/polonius/dump.rs
@@ -1,37 +1,167 @@
 use std::io;
 
-use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
-use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere};
-use rustc_middle::ty::TyCtxt;
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_index::IndexVec;
+use rustc_middle::mir::pretty::{
+    PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
+};
+use rustc_middle::mir::{Body, Location};
+use rustc_middle::ty::{RegionVid, TyCtxt};
+use rustc_mir_dataflow::points::PointIndex;
 use rustc_session::config::MirIncludeSpans;
 
 use crate::borrow_set::BorrowSet;
-use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet};
-use crate::{BorrowckInferCtxt, RegionInferenceContext};
+use crate::constraints::OutlivesConstraint;
+use crate::polonius::{
+    LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext,
+};
+use crate::region_infer::values::LivenessValues;
+use crate::type_check::Locations;
+use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext};
 
 /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
-// Note: this currently duplicates most of NLL MIR, with some additions for the localized outlives
-// constraints. This is ok for now as this dump will change in the near future to an HTML file to
-// become more useful.
 pub(crate) fn dump_polonius_mir<'tcx>(
     infcx: &BorrowckInferCtxt<'tcx>,
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
-    borrow_set: &BorrowSet<'tcx>,
-    localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>,
     closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
+    borrow_set: &BorrowSet<'tcx>,
+    polonius_diagnostics: Option<&PoloniusDiagnosticsContext>,
 ) {
     let tcx = infcx.tcx;
     if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
         return;
     }
 
-    let localized_outlives_constraints = localized_outlives_constraints
-        .expect("missing localized constraints with `-Zpolonius=next`");
+    if !dump_enabled(tcx, "polonius", body.source.def_id()) {
+        return;
+    }
+
+    let polonius_diagnostics =
+        polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
+
+    let _: io::Result<()> = try {
+        let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
+        emit_polonius_dump(
+            tcx,
+            body,
+            regioncx,
+            borrow_set,
+            &polonius_diagnostics.localized_outlives_constraints,
+            closure_region_requirements,
+            &mut file,
+        )?;
+    };
+}
+
+/// The polonius dump consists of:
+/// - the NLL MIR
+/// - the list of polonius localized constraints
+/// - a mermaid graph of the CFG
+/// - a mermaid graph of the NLL regions and the constraints between them
+/// - a mermaid graph of the NLL SCCs and the constraints between them
+fn emit_polonius_dump<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    regioncx: &RegionInferenceContext<'tcx>,
+    borrow_set: &BorrowSet<'tcx>,
+    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
+    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
+    out: &mut dyn io::Write,
+) -> io::Result<()> {
+    // Prepare the HTML dump file prologue.
+    writeln!(out, "<!DOCTYPE html>")?;
+    writeln!(out, "<html>")?;
+    writeln!(out, "<head><title>Polonius MIR dump</title></head>")?;
+    writeln!(out, "<body>")?;
+
+    // Section 1: the NLL + Polonius MIR.
+    writeln!(out, "<div>")?;
+    writeln!(out, "Raw MIR dump")?;
+    writeln!(out, "<pre><code>")?;
+    emit_html_mir(
+        tcx,
+        body,
+        regioncx,
+        borrow_set,
+        &localized_outlives_constraints,
+        closure_region_requirements,
+        out,
+    )?;
+    writeln!(out, "</code></pre>")?;
+    writeln!(out, "</div>")?;
+
+    // Section 2: mermaid visualization of the polonius constraint graph.
+    writeln!(out, "<div>")?;
+    writeln!(out, "Polonius constraint graph")?;
+    writeln!(out, "<pre class='mermaid'>")?;
+    let edge_count = emit_mermaid_constraint_graph(
+        borrow_set,
+        regioncx.liveness_constraints(),
+        &localized_outlives_constraints,
+        out,
+    )?;
+    writeln!(out, "</pre>")?;
+    writeln!(out, "</div>")?;
+
+    // Section 3: mermaid visualization of the CFG.
+    writeln!(out, "<div>")?;
+    writeln!(out, "Control-flow graph")?;
+    writeln!(out, "<pre class='mermaid'>")?;
+    emit_mermaid_cfg(body, out)?;
+    writeln!(out, "</pre>")?;
+    writeln!(out, "</div>")?;
+
+    // Section 4: mermaid visualization of the NLL region graph.
+    writeln!(out, "<div>")?;
+    writeln!(out, "NLL regions")?;
+    writeln!(out, "<pre class='mermaid'>")?;
+    emit_mermaid_nll_regions(regioncx, out)?;
+    writeln!(out, "</pre>")?;
+    writeln!(out, "</div>")?;
+
+    // Section 5: mermaid visualization of the NLL SCC graph.
+    writeln!(out, "<div>")?;
+    writeln!(out, "NLL SCCs")?;
+    writeln!(out, "<pre class='mermaid'>")?;
+    emit_mermaid_nll_sccs(regioncx, out)?;
+    writeln!(out, "</pre>")?;
+    writeln!(out, "</div>")?;
+
+    // Finalize the dump with the HTML epilogue.
+    writeln!(
+        out,
+        "<script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>"
+    )?;
+    writeln!(out, "<script>")?;
+    writeln!(
+        out,
+        "mermaid.initialize({{ startOnLoad: false, maxEdges: {} }});",
+        edge_count.max(100),
+    )?;
+    writeln!(out, "mermaid.run({{ querySelector: '.mermaid' }})")?;
+    writeln!(out, "</script>")?;
+    writeln!(out, "</body>")?;
+    writeln!(out, "</html>")?;
 
-    // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
-    // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
-    // they're always disabled in mir-opt tests to make working with blessed dumps easier.
+    Ok(())
+}
+
+/// Emits the polonius MIR, as escaped HTML.
+fn emit_html_mir<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'tcx>,
+    regioncx: &RegionInferenceContext<'tcx>,
+    borrow_set: &BorrowSet<'tcx>,
+    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
+    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
+    out: &mut dyn io::Write,
+) -> io::Result<()> {
+    // Buffer the regular MIR dump to be able to escape it.
+    let mut buffer = Vec::new();
+
+    // We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
+    // mir-include-spans` on the CLI still has priority.
     let options = PrettyPrintMirOptions {
         include_extra_comments: matches!(
             tcx.sess.opts.unstable_opts.mir_include_spans,
@@ -39,25 +169,45 @@ pub(crate) fn dump_polonius_mir<'tcx>(
         ),
     };
 
-    dump_mir_with_options(
+    dump_mir_to_writer(
         tcx,
-        false,
         "polonius",
         &0,
         body,
+        &mut buffer,
         |pass_where, out| {
             emit_polonius_mir(
                 tcx,
                 regioncx,
                 closure_region_requirements,
                 borrow_set,
-                &localized_outlives_constraints,
+                localized_outlives_constraints,
                 pass_where,
                 out,
             )
         },
         options,
-    );
+    )?;
+
+    // Escape the handful of characters that need it. We don't need to be particularly efficient:
+    // we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8.
+    let buffer = String::from_utf8_lossy(&buffer);
+    for ch in buffer.chars() {
+        let escaped = match ch {
+            '>' => "&gt;",
+            '<' => "&lt;",
+            '&' => "&amp;",
+            '\'' => "&#39;",
+            '"' => "&quot;",
+            _ => {
+                // The common case, no escaping needed.
+                write!(out, "{}", ch)?;
+                continue;
+            }
+        };
+        write!(out, "{}", escaped)?;
+    }
+    Ok(())
 }
 
 /// Produces the actual NLL + Polonius MIR sections to emit during the dumping process.
@@ -76,7 +226,7 @@ fn emit_polonius_mir<'tcx>(
         regioncx,
         closure_region_requirements,
         borrow_set,
-        pass_where.clone(),
+        pass_where,
         out,
     )?;
 
@@ -102,3 +252,237 @@ fn emit_polonius_mir<'tcx>(
 
     Ok(())
 }
+
+/// Emits a mermaid flowchart of the CFG blocks and edges, similar to the graphviz version.
+fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> {
+    use rustc_middle::mir::{TerminatorEdges, TerminatorKind};
+
+    // The mermaid chart type: a top-down flowchart.
+    writeln!(out, "flowchart TD")?;
+
+    // Emit the block nodes.
+    for (block_idx, block) in body.basic_blocks.iter_enumerated() {
+        let block_idx = block_idx.as_usize();
+        let cleanup = if block.is_cleanup { " (cleanup)" } else { "" };
+        writeln!(out, "{block_idx}[\"bb{block_idx}{cleanup}\"]")?;
+    }
+
+    // Emit the edges between blocks, from the terminator edges.
+    for (block_idx, block) in body.basic_blocks.iter_enumerated() {
+        let block_idx = block_idx.as_usize();
+        let terminator = block.terminator();
+        match terminator.edges() {
+            TerminatorEdges::None => {}
+            TerminatorEdges::Single(bb) => {
+                writeln!(out, "{block_idx} --> {}", bb.as_usize())?;
+            }
+            TerminatorEdges::Double(bb1, bb2) => {
+                if matches!(terminator.kind, TerminatorKind::FalseEdge { .. }) {
+                    writeln!(out, "{block_idx} --> {}", bb1.as_usize())?;
+                    writeln!(out, "{block_idx} -- imaginary --> {}", bb2.as_usize())?;
+                } else {
+                    writeln!(out, "{block_idx} --> {}", bb1.as_usize())?;
+                    writeln!(out, "{block_idx} -- unwind --> {}", bb2.as_usize())?;
+                }
+            }
+            TerminatorEdges::AssignOnReturn { return_, cleanup, .. } => {
+                for to_idx in return_ {
+                    writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?;
+                }
+
+                if let Some(to_idx) = cleanup {
+                    writeln!(out, "{block_idx} -- unwind --> {}", to_idx.as_usize())?;
+                }
+            }
+            TerminatorEdges::SwitchInt { targets, .. } => {
+                for to_idx in targets.all_targets() {
+                    writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?;
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// Emits a region's label: index, universe, external name.
+fn render_region(
+    region: RegionVid,
+    regioncx: &RegionInferenceContext<'_>,
+    out: &mut dyn io::Write,
+) -> io::Result<()> {
+    let def = regioncx.region_definition(region);
+    let universe = def.universe;
+
+    write!(out, "'{}", region.as_usize())?;
+    if !universe.is_root() {
+        write!(out, "/{universe:?}")?;
+    }
+    if let Some(name) = def.external_name.and_then(|e| e.get_name()) {
+        write!(out, " ({name})")?;
+    }
+    Ok(())
+}
+
+/// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar
+/// to the graphviz version.
+fn emit_mermaid_nll_regions<'tcx>(
+    regioncx: &RegionInferenceContext<'tcx>,
+    out: &mut dyn io::Write,
+) -> io::Result<()> {
+    // The mermaid chart type: a top-down flowchart.
+    writeln!(out, "flowchart TD")?;
+
+    // Emit the region nodes.
+    for region in regioncx.definitions.indices() {
+        write!(out, "{}[\"", region.as_usize())?;
+        render_region(region, regioncx, out)?;
+        writeln!(out, "\"]")?;
+    }
+
+    // Get a set of edges to check for the reverse edge being present.
+    let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect();
+
+    // Order (and deduplicate) edges for traversal, to display them in a generally increasing order.
+    let constraint_key = |c: &OutlivesConstraint<'_>| {
+        let min = c.sup.min(c.sub);
+        let max = c.sup.max(c.sub);
+        (min, max)
+    };
+    let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect();
+    ordered_edges.sort_by_key(|c| constraint_key(c));
+    ordered_edges.dedup_by_key(|c| constraint_key(c));
+
+    for outlives in ordered_edges {
+        // Source node.
+        write!(out, "{} ", outlives.sup.as_usize())?;
+
+        // The kind of arrow: bidirectional if the opposite edge exists in the set.
+        if edges.contains(&(outlives.sub, outlives.sup)) {
+            write!(out, "&lt;")?;
+        }
+        write!(out, "-- ")?;
+
+        // Edge label from its `Locations`.
+        match outlives.locations {
+            Locations::All(_) => write!(out, "All")?,
+            Locations::Single(location) => write!(out, "{:?}", location)?,
+        }
+
+        // Target node.
+        writeln!(out, " --> {}", outlives.sub.as_usize())?;
+    }
+    Ok(())
+}
+
+/// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar
+/// to the graphviz version.
+fn emit_mermaid_nll_sccs<'tcx>(
+    regioncx: &RegionInferenceContext<'tcx>,
+    out: &mut dyn io::Write,
+) -> io::Result<()> {
+    // The mermaid chart type: a top-down flowchart.
+    writeln!(out, "flowchart TD")?;
+
+    // Gather and emit the SCC nodes.
+    let mut nodes_per_scc: IndexVec<_, _> =
+        regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect();
+    for region in regioncx.definitions.indices() {
+        let scc = regioncx.constraint_sccs().scc(region);
+        nodes_per_scc[scc].push(region);
+    }
+    for (scc, regions) in nodes_per_scc.iter_enumerated() {
+        // The node label: the regions contained in the SCC.
+        write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?;
+        for (idx, &region) in regions.iter().enumerate() {
+            render_region(region, regioncx, out)?;
+            if idx < regions.len() - 1 {
+                write!(out, ",")?;
+            }
+        }
+        writeln!(out, "}}\"]")?;
+    }
+
+    // Emit the edges between SCCs.
+    let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| {
+        regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target))
+    });
+    for (source, target) in edges {
+        writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?;
+    }
+
+    Ok(())
+}
+
+/// Emits a mermaid flowchart of the polonius localized outlives constraints, with subgraphs per
+/// region, and loan introductions.
+fn emit_mermaid_constraint_graph<'tcx>(
+    borrow_set: &BorrowSet<'tcx>,
+    liveness: &LivenessValues,
+    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
+    out: &mut dyn io::Write,
+) -> io::Result<usize> {
+    let location_name = |location: Location| {
+        // A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id,
+        // transform it into `BB5_2`.
+        format!("BB{}_{}", location.block.index(), location.statement_index)
+    };
+    let region_name = |region: RegionVid| format!("'{}", region.index());
+    let node_name = |region: RegionVid, point: PointIndex| {
+        let location = liveness.location_from_point(point);
+        format!("{}_{}", region_name(region), location_name(location))
+    };
+
+    // The mermaid chart type: a top-down flowchart, which supports subgraphs.
+    writeln!(out, "flowchart TD")?;
+
+    // The loans subgraph: a node per loan.
+    writeln!(out, "    subgraph \"Loans\"")?;
+    for loan_idx in 0..borrow_set.len() {
+        writeln!(out, "        L{loan_idx}")?;
+    }
+    writeln!(out, "    end\n")?;
+
+    // And an edge from that loan node to where it enters the constraint graph.
+    for (loan_idx, loan) in borrow_set.iter_enumerated() {
+        writeln!(
+            out,
+            "    L{} --> {}_{}",
+            loan_idx.index(),
+            region_name(loan.region),
+            location_name(loan.reserve_location),
+        )?;
+    }
+    writeln!(out, "")?;
+
+    // The regions subgraphs containing the region/point nodes.
+    let mut points_per_region: FxIndexMap<RegionVid, FxIndexSet<PointIndex>> =
+        FxIndexMap::default();
+    for constraint in &localized_outlives_constraints.outlives {
+        points_per_region.entry(constraint.source).or_default().insert(constraint.from);
+        points_per_region.entry(constraint.target).or_default().insert(constraint.to);
+    }
+    for (region, points) in points_per_region {
+        writeln!(out, "    subgraph \"{}\"", region_name(region))?;
+        for point in points {
+            writeln!(out, "        {}", node_name(region, point))?;
+        }
+        writeln!(out, "    end\n")?;
+    }
+
+    // The constraint graph edges.
+    for constraint in &localized_outlives_constraints.outlives {
+        // FIXME: add killed loans and constraint kind as edge labels.
+        writeln!(
+            out,
+            "    {} --> {}",
+            node_name(constraint.source, constraint.from),
+            node_name(constraint.target, constraint.to),
+        )?;
+    }
+
+    // Return the number of edges: this is the biggest graph in the dump and its edge count will be
+    // mermaid's max edge count to support.
+    let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len();
+    Ok(edge_count)
+}
diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
index cbcfab1dc3e1..0d1d8642bcac 100644
--- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
+++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs
@@ -3,11 +3,7 @@ use std::ops::ControlFlow;
 use rustc_data_structures::graph::dominators::Dominators;
 use rustc_middle::bug;
 use rustc_middle::mir::visit::Visitor;
-use rustc_middle::mir::{
-    self, BasicBlock, Body, BorrowKind, FakeBorrowKind, InlineAsmOperand, Location, Mutability,
-    NonDivergingIntrinsic, Operand, Place, Rvalue, Statement, StatementKind, Terminator,
-    TerminatorKind,
-};
+use rustc_middle::mir::*;
 use rustc_middle::ty::TyCtxt;
 use tracing::debug;
 
@@ -60,7 +56,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
             StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
                 self.consume_operand(location, op);
             }
-            StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping {
+            StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
                 src,
                 dst,
                 count,
@@ -273,15 +269,18 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
                 self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
             }
 
-            &Rvalue::RawPtr(mutability, place) => {
-                let access_kind = match mutability {
-                    Mutability::Mut => (
+            &Rvalue::RawPtr(kind, place) => {
+                let access_kind = match kind {
+                    RawPtrKind::Mut => (
                         Deep,
                         Write(WriteKind::MutableBorrow(BorrowKind::Mut {
-                            kind: mir::MutBorrowKind::Default,
+                            kind: MutBorrowKind::Default,
                         })),
                     ),
-                    Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
+                    RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
+                    RawPtrKind::FakeForPtrMetadata => {
+                        (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy))
+                    }
                 };
 
                 self.access_place(location, place, access_kind, LocalMutationIsAllowed::No);
@@ -326,6 +325,10 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
                     self.consume_operand(location, operand);
                 }
             }
+
+            Rvalue::WrapUnsafeBinder(op, _) => {
+                self.consume_operand(location, op);
+            }
         }
     }
 
diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
index 75ee29c9d0d5..6ab09f731c07 100644
--- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
+++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
@@ -1,7 +1,6 @@
 use std::collections::BTreeMap;
 
 use rustc_index::bit_set::SparseBitMatrix;
-use rustc_index::interval::SparseIntervalMatrix;
 use rustc_middle::mir::{Body, Location};
 use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
@@ -9,12 +8,12 @@ use rustc_mir_dataflow::points::PointIndex;
 
 use super::{
     ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
-    PoloniusContext,
+    PoloniusLivenessContext,
 };
 use crate::region_infer::values::LivenessValues;
 use crate::universal_regions::UniversalRegions;
 
-impl PoloniusContext {
+impl PoloniusLivenessContext {
     /// Record the variance of each region contained within the given value.
     pub(crate) fn record_live_region_variance<'tcx>(
         &mut self,
@@ -30,25 +29,6 @@ impl PoloniusContext {
         };
         extractor.relate(value, value).expect("Can't have a type error relating to itself");
     }
-
-    /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
-    /// need to transpose the "points where each region is live" matrix to a "live regions per point"
-    /// matrix.
-    // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
-    // borrowck.
-    pub(crate) fn record_live_regions_per_point(
-        &mut self,
-        num_regions: usize,
-        points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
-    ) {
-        let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
-        for region in points_per_live_region.rows() {
-            for point in points_per_live_region.row(region).unwrap().iter() {
-                live_regions_per_point.insert(point, region);
-            }
-        }
-        self.live_regions = Some(live_regions_per_point);
-    }
 }
 
 /// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
diff --git a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
index 768c12a97a64..5cd265e0db92 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
+++ b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs
@@ -175,7 +175,7 @@ impl LocalizedConstraintGraph {
     }
 
     /// Returns the outgoing edges of a given node, not its transitive closure.
-    fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> + use<'_> {
+    fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> {
         // The outgoing edges are:
         // - the physical edges present at this node,
         // - the materialized logical edges that exist virtually at all points for this node's
diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs
index 502c868194af..142ef8ba28ef 100644
--- a/compiler/rustc_borrowck/src/polonius/mod.rs
+++ b/compiler/rustc_borrowck/src/polonius/mod.rs
@@ -32,6 +32,16 @@
 //! - <https://smallcultfollowing.com/babysteps/blog/2023/09/22/polonius-part-1/>
 //! - <https://smallcultfollowing.com/babysteps/blog/2023/09/29/polonius-part-2/>
 //!
+//!
+//! Data flows like this:
+//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the
+//!    usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext].
+//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to
+//!    the polonius shape. That's the main [PoloniusContext].
+//! 3) during region inference, that data and the NLL outlives constraints are used to create the
+//!    localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext].
+//! 4) transfer this back to the main borrowck procedure: it handles computing errors and
+//!    diagnostics, debugging and MIR dumping concerns.
 
 mod constraints;
 mod dump;
@@ -42,8 +52,10 @@ mod typeck_constraints;
 
 use std::collections::BTreeMap;
 
+use rustc_data_structures::fx::FxHashSet;
 use rustc_index::bit_set::SparseBitMatrix;
-use rustc_middle::mir::Body;
+use rustc_index::interval::SparseIntervalMatrix;
+use rustc_middle::mir::{Body, Local};
 use rustc_middle::ty::{RegionVid, TyCtxt};
 use rustc_mir_dataflow::points::PointIndex;
 
@@ -57,15 +69,40 @@ use crate::{BorrowSet, RegionInferenceContext};
 
 pub(crate) type LiveLoans = SparseBitMatrix<PointIndex, BorrowIndex>;
 
-/// This struct holds the data needed to create the Polonius localized constraints.
+/// This struct holds the liveness data created during MIR typeck, and which will be used later in
+/// the process, to compute the polonius localized constraints.
+#[derive(Default)]
+pub(crate) struct PoloniusLivenessContext {
+    /// The expected edge direction per live region: the kind of directed edge we'll create as
+    /// liveness constraints depends on the variance of types with respect to each contained region.
+    live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
+
+    /// The regions that outlive free regions are used to distinguish relevant live locals from
+    /// boring locals. A boring local is one whose type contains only such regions. Polonius
+    /// currently has more boring locals than NLLs so we record the latter to use in errors and
+    /// diagnostics, to focus on the locals we consider relevant and match NLL diagnostics.
+    pub(crate) boring_nll_locals: FxHashSet<Local>,
+}
+
+/// This struct holds the data needed to create the Polonius localized constraints. Its data is
+/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck.
 pub(crate) struct PoloniusContext {
+    /// The liveness data we recorded during MIR typeck.
+    liveness_context: PoloniusLivenessContext,
+
     /// The set of regions that are live at a given point in the CFG, used to create localized
     /// outlives constraints between regions that are live at connected points in the CFG.
-    live_regions: Option<SparseBitMatrix<PointIndex, RegionVid>>,
+    live_regions: SparseBitMatrix<PointIndex, RegionVid>,
+}
 
-    /// The expected edge direction per live region: the kind of directed edge we'll create as
-    /// liveness constraints depends on the variance of types with respect to each contained region.
-    live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
+/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is
+/// computed from the [PoloniusContext] when computing NLL regions.
+pub(crate) struct PoloniusDiagnosticsContext {
+    /// The localized outlives constraints that were computed in the main analysis.
+    localized_outlives_constraints: LocalizedOutlivesConstraintSet,
+
+    /// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals].
+    pub(crate) boring_nll_locals: FxHashSet<Local>,
 }
 
 /// The direction a constraint can flow into. Used to create liveness constraints according to
@@ -83,8 +120,24 @@ enum ConstraintDirection {
 }
 
 impl PoloniusContext {
-    pub(crate) fn new() -> PoloniusContext {
-        Self { live_region_variances: BTreeMap::new(), live_regions: None }
+    /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
+    /// need to transpose the "points where each region is live" matrix to a "live regions per point"
+    /// matrix.
+    // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
+    // borrowck.
+    pub(crate) fn create_from_liveness(
+        liveness_context: PoloniusLivenessContext,
+        num_regions: usize,
+        points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
+    ) -> PoloniusContext {
+        let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
+        for region in points_per_live_region.rows() {
+            for point in points_per_live_region.row(region).unwrap().iter() {
+                live_regions_per_point.insert(point, region);
+            }
+        }
+
+        PoloniusContext { live_regions: live_regions_per_point, liveness_context }
     }
 
     /// Computes live loans using the set of loans model for `-Zpolonius=next`.
@@ -95,13 +148,18 @@ impl PoloniusContext {
     ///
     /// Then, this graph is traversed, and combined with kills, reachability is recorded as loan
     /// liveness, to be used by the loan scope and active loans computations.
+    ///
+    /// The constraint data will be used to compute errors and diagnostics.
     pub(crate) fn compute_loan_liveness<'tcx>(
-        &self,
+        self,
         tcx: TyCtxt<'tcx>,
         regioncx: &mut RegionInferenceContext<'tcx>,
         body: &Body<'tcx>,
         borrow_set: &BorrowSet<'tcx>,
-    ) -> LocalizedOutlivesConstraintSet {
+    ) -> PoloniusDiagnosticsContext {
+        let PoloniusLivenessContext { live_region_variances, boring_nll_locals } =
+            self.liveness_context;
+
         let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
         convert_typeck_constraints(
             tcx,
@@ -112,14 +170,11 @@ impl PoloniusContext {
             &mut localized_outlives_constraints,
         );
 
-        let live_regions = self.live_regions.as_ref().expect(
-            "live regions per-point data should have been created at the end of MIR typeck",
-        );
         create_liveness_constraints(
             body,
             regioncx.liveness_constraints(),
-            live_regions,
-            &self.live_region_variances,
+            &self.live_regions,
+            &live_region_variances,
             regioncx.universal_regions(),
             &mut localized_outlives_constraints,
         );
@@ -136,6 +191,6 @@ impl PoloniusContext {
         );
         regioncx.record_live_loans(live_loans);
 
-        localized_outlives_constraints
+        PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals }
     }
 }
diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs
index fc7e6e586410..83cca38a5c09 100644
--- a/compiler/rustc_borrowck/src/prefixes.rs
+++ b/compiler/rustc_borrowck/src/prefixes.rs
@@ -66,6 +66,10 @@ impl<'tcx> Iterator for Prefixes<'tcx> {
                             self.next = Some(cursor_base);
                             return Some(cursor);
                         }
+                        ProjectionElem::UnwrapUnsafeBinder(_) => {
+                            self.next = Some(cursor_base);
+                            return Some(cursor);
+                        }
                         ProjectionElem::Downcast(..)
                         | ProjectionElem::Subslice { .. }
                         | ProjectionElem::OpaqueCast { .. }
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index d2268c4779d6..f8af9e59f635 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -9,23 +9,20 @@ use rustc_errors::Diag;
 use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_index::IndexVec;
 use rustc_infer::infer::outlives::test_type_match;
-use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
+use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound, VerifyIfEq};
 use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
 use rustc_middle::bug;
 use rustc_middle::mir::{
-    AnnotationSource, BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject,
-    ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location,
-    ReturnConstraint, TerminatorKind,
+    AnnotationSource, BasicBlock, Body, ConstraintCategory, Local, Location, ReturnConstraint,
+    TerminatorKind,
 };
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
-use rustc_middle::ty::fold::fold_regions;
-use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex};
+use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, fold_regions};
 use rustc_mir_dataflow::points::DenseLocationMap;
-use rustc_span::Span;
 use rustc_span::hygiene::DesugaringKind;
-use tracing::{debug, instrument, trace};
+use rustc_span::{DUMMY_SP, Span};
+use tracing::{Level, debug, enabled, instrument, trace};
 
-use crate::BorrowckInferCtxt;
 use crate::constraints::graph::{self, NormalConstraintGraph, RegionGraph};
 use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet};
 use crate::dataflow::BorrowIndex;
@@ -38,6 +35,10 @@ use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, T
 use crate::type_check::free_region_relations::UniversalRegionRelations;
 use crate::type_check::{Locations, MirTypeckRegionConstraints};
 use crate::universal_regions::UniversalRegions;
+use crate::{
+    BorrowckInferCtxt, ClosureOutlivesRequirement, ClosureOutlivesSubject,
+    ClosureOutlivesSubjectTy, ClosureRegionRequirements,
+};
 
 mod dump_mir;
 mod graphviz;
@@ -140,13 +141,11 @@ impl RegionTracker {
 }
 
 pub struct RegionInferenceContext<'tcx> {
-    pub var_infos: VarInfos,
-
     /// Contains the definition for every region variable. Region
     /// variables are identified by their index (`RegionVid`). The
     /// definition contains information about where the region came
     /// from as well as its final inferred value.
-    definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
+    pub(crate) definitions: Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>>,
 
     /// The liveness constraints added to each region. For most
     /// regions, these start out empty and steadily grow, though for
@@ -311,9 +310,11 @@ enum RegionRelationCheckResult {
 }
 
 #[derive(Clone, PartialEq, Eq, Debug)]
-enum Trace<'tcx> {
+enum Trace<'a, 'tcx> {
     StartRegion,
-    FromOutlivesConstraint(OutlivesConstraint<'tcx>),
+    FromGraph(&'a OutlivesConstraint<'tcx>),
+    FromStatic(RegionVid),
+    FromMember(RegionVid, RegionVid, Span),
     NotVisited,
 }
 
@@ -326,31 +327,34 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
     let mut var_to_origin_sorted = var_to_origin.clone().into_iter().collect::<Vec<_>>();
     var_to_origin_sorted.sort_by_key(|vto| vto.0);
 
-    let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string();
-    for (reg_var, origin) in var_to_origin_sorted.into_iter() {
-        reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n"));
+    if enabled!(Level::DEBUG) {
+        let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string();
+        for (reg_var, origin) in var_to_origin_sorted.into_iter() {
+            reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n"));
+        }
+        debug!("{}", reg_vars_to_origins_str);
     }
-    debug!("{}", reg_vars_to_origins_str);
 
     let num_components = sccs.num_sccs();
     let mut components = vec![FxIndexSet::default(); num_components];
 
-    for (reg_var_idx, scc_idx) in sccs.scc_indices().iter().enumerate() {
-        let reg_var = ty::RegionVid::from_usize(reg_var_idx);
+    for (reg_var, scc_idx) in sccs.scc_indices().iter_enumerated() {
         let origin = var_to_origin.get(&reg_var).unwrap_or(&RegionCtxt::Unknown);
         components[scc_idx.as_usize()].insert((reg_var, *origin));
     }
 
-    let mut components_str = "strongly connected components:".to_string();
-    for (scc_idx, reg_vars_origins) in components.iter().enumerate() {
-        let regions_info = reg_vars_origins.clone().into_iter().collect::<Vec<_>>();
-        components_str.push_str(&format!(
-            "{:?}: {:?},\n)",
-            ConstraintSccIndex::from_usize(scc_idx),
-            regions_info,
-        ))
+    if enabled!(Level::DEBUG) {
+        let mut components_str = "strongly connected components:".to_string();
+        for (scc_idx, reg_vars_origins) in components.iter().enumerate() {
+            let regions_info = reg_vars_origins.clone().into_iter().collect::<Vec<_>>();
+            components_str.push_str(&format!(
+                "{:?}: {:?},\n)",
+                ConstraintSccIndex::from_usize(scc_idx),
+                regions_info,
+            ))
+        }
+        debug!("{}", components_str);
     }
-    debug!("{}", components_str);
 
     // calculate the best representative for each component
     let components_representatives = components
@@ -380,6 +384,26 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
     debug!("SCC edges {:#?}", scc_node_to_edges);
 }
 
+fn create_definitions<'tcx>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    universal_regions: &UniversalRegions<'tcx>,
+) -> Frozen<IndexVec<RegionVid, RegionDefinition<'tcx>>> {
+    // Create a RegionDefinition for each inference variable.
+    let mut definitions: IndexVec<_, _> = infcx
+        .get_region_var_infos()
+        .iter()
+        .map(|info| RegionDefinition::new(info.universe, info.origin))
+        .collect();
+
+    // Add the external name for all universal regions.
+    for (external_name, variable) in universal_regions.named_universal_regions_iter() {
+        debug!("region {variable:?} has external name {external_name:?}");
+        definitions[variable].external_name = Some(external_name);
+    }
+
+    Frozen::freeze(definitions)
+}
+
 impl<'tcx> RegionInferenceContext<'tcx> {
     /// Creates a new region inference context with a total of
     /// `num_region_variables` valid inference variables; the first N
@@ -390,7 +414,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// of constraints produced by the MIR type check.
     pub(crate) fn new(
         infcx: &BorrowckInferCtxt<'tcx>,
-        var_infos: VarInfos,
         constraints: MirTypeckRegionConstraints<'tcx>,
         universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
         location_map: Rc<DenseLocationMap>,
@@ -421,11 +444,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             infcx.set_tainted_by_errors(guar);
         }
 
-        // Create a RegionDefinition for each inference variable.
-        let definitions: IndexVec<_, _> = var_infos
-            .iter()
-            .map(|info| RegionDefinition::new(info.universe, info.origin))
-            .collect();
+        let definitions = create_definitions(infcx, &universal_regions);
 
         let constraint_sccs =
             outlives_constraints.add_outlives_static(&universal_regions, &definitions);
@@ -448,7 +467,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             Rc::new(member_constraints.into_mapped(|r| constraint_sccs.scc(r)));
 
         let mut result = Self {
-            var_infos,
             definitions,
             liveness_constraints,
             constraints,
@@ -522,18 +540,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// means that the `R1: !1` constraint here will cause
     /// `R1` to become `'static`.
     fn init_free_and_bound_regions(&mut self) {
-        // Update the names (if any)
-        // This iterator has unstable order but we collect it all into an IndexVec
-        for (external_name, variable) in
-            self.universal_region_relations.universal_regions.named_universal_regions_iter()
-        {
-            debug!(
-                "init_free_and_bound_regions: region {:?} has external name {:?}",
-                variable, external_name
-            );
-            self.definitions[variable].external_name = Some(external_name);
-        }
-
         for variable in self.definitions.indices() {
             let scc = self.constraint_sccs.scc(variable);
 
@@ -576,9 +582,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     }
 
     /// Returns an iterator over all the outlives constraints.
-    pub(crate) fn outlives_constraints(
-        &self,
-    ) -> impl Iterator<Item = OutlivesConstraint<'tcx>> + '_ {
+    pub(crate) fn outlives_constraints(&self) -> impl Iterator<Item = OutlivesConstraint<'tcx>> {
         self.constraints.outlives().iter().copied()
     }
 
@@ -615,10 +619,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.scc_values.region_value_str(scc)
     }
 
-    pub(crate) fn placeholders_contained_in<'a>(
-        &'a self,
+    pub(crate) fn placeholders_contained_in(
+        &self,
         r: RegionVid,
-    ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
+    ) -> impl Iterator<Item = ty::PlaceholderRegion> {
         let scc = self.constraint_sccs.scc(r);
         self.scc_values.placeholders_contained_in(scc)
     }
@@ -1267,6 +1271,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let sub_region_scc = self.constraint_sccs.scc(sub_region);
         let sup_region_scc = self.constraint_sccs.scc(sup_region);
 
+        if sub_region_scc == sup_region_scc {
+            debug!("{sup_region:?}: {sub_region:?} holds trivially; they are in the same SCC");
+            return true;
+        }
+
         // If we are checking that `'sup: 'sub`, and `'sub` contains
         // some placeholder that `'sup` cannot name, then this is only
         // true if `'sup` outlives static.
@@ -1761,6 +1770,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
         context[from_region] = Trace::StartRegion;
 
+        let fr_static = self.universal_regions().fr_static;
+
         // Use a deque so that we do a breadth-first search. We will
         // stop at the first match, which ought to be the shortest
         // path (fewest constraints).
@@ -1780,13 +1791,39 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             if target_test(r) {
                 let mut result = vec![];
                 let mut p = r;
+                // This loop is cold and runs at the end, which is why we delay
+                // `OutlivesConstraint` construction until now.
                 loop {
-                    match context[p].clone() {
-                        Trace::NotVisited => {
-                            bug!("found unvisited region {:?} on path to {:?}", p, r)
+                    match context[p] {
+                        Trace::FromGraph(c) => {
+                            p = c.sup;
+                            result.push(*c);
                         }
 
-                        Trace::FromOutlivesConstraint(c) => {
+                        Trace::FromStatic(sub) => {
+                            let c = OutlivesConstraint {
+                                sup: fr_static,
+                                sub,
+                                locations: Locations::All(DUMMY_SP),
+                                span: DUMMY_SP,
+                                category: ConstraintCategory::Internal,
+                                variance_info: ty::VarianceDiagInfo::default(),
+                                from_closure: false,
+                            };
+                            p = c.sup;
+                            result.push(c);
+                        }
+
+                        Trace::FromMember(sup, sub, span) => {
+                            let c = OutlivesConstraint {
+                                sup,
+                                sub,
+                                locations: Locations::All(span),
+                                span,
+                                category: ConstraintCategory::OpaqueType,
+                                variance_info: ty::VarianceDiagInfo::default(),
+                                from_closure: false,
+                            };
                             p = c.sup;
                             result.push(c);
                         }
@@ -1795,6 +1832,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                             result.reverse();
                             return Some((result, r));
                         }
+
+                        Trace::NotVisited => {
+                            bug!("found unvisited region {:?} on path to {:?}", p, r)
+                        }
                     }
                 }
             }
@@ -1805,45 +1846,42 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
             // A constraint like `'r: 'x` can come from our constraint
             // graph.
-            let fr_static = self.universal_regions().fr_static;
-            let outgoing_edges_from_graph =
-                self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static);
 
             // Always inline this closure because it can be hot.
-            let mut handle_constraint = #[inline(always)]
-            |constraint: OutlivesConstraint<'tcx>| {
-                debug_assert_eq!(constraint.sup, r);
-                let sub_region = constraint.sub;
-                if let Trace::NotVisited = context[sub_region] {
-                    context[sub_region] = Trace::FromOutlivesConstraint(constraint);
-                    deque.push_back(sub_region);
+            let mut handle_trace = #[inline(always)]
+            |sub, trace| {
+                if let Trace::NotVisited = context[sub] {
+                    context[sub] = trace;
+                    deque.push_back(sub);
                 }
             };
 
-            // This loop can be hot.
-            for constraint in outgoing_edges_from_graph {
-                if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
-                    debug!("Ignoring illegal universe constraint: {constraint:?}");
-                    continue;
+            // If this is the `'static` region and the graph's direction is normal, then set up the
+            // Edges iterator to return all regions (#53178).
+            if r == fr_static && self.constraint_graph.is_normal() {
+                for sub in self.constraint_graph.outgoing_edges_from_static() {
+                    handle_trace(sub, Trace::FromStatic(sub));
+                }
+            } else {
+                let edges = self.constraint_graph.outgoing_edges_from_graph(r, &self.constraints);
+                // This loop can be hot.
+                for constraint in edges {
+                    if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
+                        debug!("Ignoring illegal universe constraint: {constraint:?}");
+                        continue;
+                    }
+                    debug_assert_eq!(constraint.sup, r);
+                    handle_trace(constraint.sub, Trace::FromGraph(constraint));
                 }
-                handle_constraint(constraint);
             }
 
             // Member constraints can also give rise to `'r: 'x` edges that
             // were not part of the graph initially, so watch out for those.
             // (But they are extremely rare; this loop is very cold.)
             for constraint in self.applied_member_constraints(self.constraint_sccs.scc(r)) {
+                let sub = constraint.min_choice;
                 let p_c = &self.member_constraints[constraint.member_constraint_index];
-                let constraint = OutlivesConstraint {
-                    sup: r,
-                    sub: constraint.min_choice,
-                    locations: Locations::All(p_c.definition_span),
-                    span: p_c.definition_span,
-                    category: ConstraintCategory::OpaqueType,
-                    variance_info: ty::VarianceDiagInfo::default(),
-                    from_closure: false,
-                };
-                handle_constraint(constraint);
+                handle_trace(sub, Trace::FromMember(r, sub, p_c.definition_span));
             }
         }
 
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 7c484327e311..550c57338d30 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -1,20 +1,17 @@
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::ErrorGuaranteed;
-use rustc_hir::def_id::LocalDefId;
-use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
+use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
 use rustc_macros::extension;
-use rustc_middle::ty::fold::fold_regions;
-use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{
-    self, GenericArgKind, GenericArgs, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
-    TypingMode,
+    self, DefiningScopeKind, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable,
+    TypeVisitableExt, fold_regions,
 };
 use rustc_span::Span;
-use rustc_trait_selection::traits::ObligationCtxt;
+use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
 use tracing::{debug, instrument};
 
 use super::RegionInferenceContext;
-use crate::session_diagnostics::{LifetimeMismatchOpaqueParam, NonGenericOpaqueTypeParam};
+use crate::BorrowCheckRootCtxt;
+use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
 use crate::universal_regions::RegionClassification;
 
 impl<'tcx> RegionInferenceContext<'tcx> {
@@ -61,13 +58,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ///
     /// [rustc-dev-guide chapter]:
     /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
-    #[instrument(level = "debug", skip(self, infcx), ret)]
+    #[instrument(level = "debug", skip(self, root_cx, infcx), ret)]
     pub(crate) fn infer_opaque_types(
         &self,
+        root_cx: &mut BorrowCheckRootCtxt<'tcx>,
         infcx: &InferCtxt<'tcx>,
         opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
-    ) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
-        let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
+    ) {
         let mut decls_modulo_regions: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueTypeKey<'tcx>, Span)> =
             FxIndexMap::default();
 
@@ -142,32 +139,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     continue;
                 }
             }
-            // Sometimes two opaque types are the same only after we remap the generic parameters
-            // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to
-            // `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we
-            // only know that once we convert the generic parameters to those of the opaque type.
-            if let Some(prev) = result.get_mut(&opaque_type_key.def_id) {
-                if prev.ty != ty {
-                    let guar = ty.error_reported().err().unwrap_or_else(|| {
-                        let (Ok(e) | Err(e)) = prev
-                            .build_mismatch_error(
-                                &OpaqueHiddenType { ty, span: concrete_type.span },
-                                infcx.tcx,
-                            )
-                            .map(|d| d.emit());
-                        e
-                    });
-                    prev.ty = Ty::new_error(infcx.tcx, guar);
-                }
-                // Pick a better span if there is one.
-                // FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
-                prev.span = prev.span.substitute_dummy(concrete_type.span);
-            } else {
-                result.insert(opaque_type_key.def_id, OpaqueHiddenType {
-                    ty,
-                    span: concrete_type.span,
-                });
-            }
+
+            root_cx.add_concrete_opaque_type(
+                opaque_type_key.def_id,
+                OpaqueHiddenType { span: concrete_type.span, ty },
+            );
 
             // Check that all opaque types have the same region parameters if they have the same
             // non-region parameters. This is necessary because within the new solver we perform
@@ -192,7 +168,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 });
             }
         }
-        result
     }
 
     /// Map the regions in the type to named regions. This is similar to what
@@ -201,11 +176,17 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// that the regions produced are in fact equal to the named region they are
     /// replaced with. This is fine because this function is only to improve the
     /// region names in error messages.
-    pub(crate) fn name_regions<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
+    ///
+    /// This differs from `MirBorrowckCtxt::name_regions` since it is particularly
+    /// lax with mapping region vids that are *shorter* than a universal region to
+    /// that universal region. This is useful for member region constraints since
+    /// we want to suggest a universal region name to capture even if it's technically
+    /// not equal to the error region.
+    pub(crate) fn name_regions_for_member_constraint<T>(&self, tcx: TyCtxt<'tcx>, ty: T) -> T
     where
         T: TypeFoldable<TyCtxt<'tcx>>,
     {
-        fold_regions(tcx, ty, |region, _| match *region {
+        fold_regions(tcx, ty, |region, _| match region.kind() {
             ty::ReVar(vid) => {
                 let scc = self.constraint_sccs.scc(vid);
 
@@ -286,14 +267,21 @@ impl<'tcx> InferCtxt<'tcx> {
             return Ty::new_error(self.tcx, e);
         }
 
-        if let Err(guar) =
-            check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span)
-        {
+        if let Err(guar) = check_opaque_type_parameter_valid(
+            self,
+            opaque_type_key,
+            instantiated_ty.span,
+            DefiningScopeKind::MirBorrowck,
+        ) {
             return Ty::new_error(self.tcx, guar);
         }
 
         let definition_ty = instantiated_ty
-            .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
+            .remap_generic_params_to_declaration_params(
+                opaque_type_key,
+                self.tcx,
+                DefiningScopeKind::MirBorrowck,
+            )
             .ty;
 
         if let Err(e) = definition_ty.error_reported() {
@@ -303,161 +291,3 @@ impl<'tcx> InferCtxt<'tcx> {
         definition_ty
     }
 }
-
-/// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
-///
-/// [rustc-dev-guide chapter]:
-/// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
-fn check_opaque_type_parameter_valid<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    opaque_type_key: OpaqueTypeKey<'tcx>,
-    span: Span,
-) -> Result<(), ErrorGuaranteed> {
-    let tcx = infcx.tcx;
-    let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
-    let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
-    let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
-
-    for (i, arg) in opaque_type_key.iter_captured_args(tcx) {
-        let arg_is_param = match arg.unpack() {
-            GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
-            GenericArgKind::Lifetime(lt) => {
-                matches!(*lt, ty::ReEarlyParam(_) | ty::ReLateParam(_))
-                    || (lt.is_static() && opaque_env.param_equal_static(i))
-            }
-            GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
-        };
-
-        if arg_is_param {
-            // Register if the same lifetime appears multiple times in the generic args.
-            // There is an exception when the opaque type *requires* the lifetimes to be equal.
-            // See [rustc-dev-guide chapter] § "An exception to uniqueness rule".
-            let seen_where = seen_params.entry(arg).or_default();
-            if !seen_where.first().is_some_and(|&prev_i| opaque_env.params_equal(i, prev_i)) {
-                seen_where.push(i);
-            }
-        } else {
-            // Prevent `fn foo() -> Foo<u32>` from being defining.
-            let opaque_param = opaque_generics.param_at(i, tcx);
-            let kind = opaque_param.kind.descr();
-
-            opaque_env.param_is_error(i)?;
-
-            return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
-                ty: arg,
-                kind,
-                span,
-                param_span: tcx.def_span(opaque_param.def_id),
-            }));
-        }
-    }
-
-    for (_, indices) in seen_params {
-        if indices.len() > 1 {
-            let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
-            let spans: Vec<_> = indices
-                .into_iter()
-                .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
-                .collect();
-            #[allow(rustc::diagnostic_outside_of_impl)]
-            #[allow(rustc::untranslatable_diagnostic)]
-            return Err(infcx
-                .dcx()
-                .struct_span_err(span, "non-defining opaque type use in defining scope")
-                .with_span_note(spans, format!("{descr} used multiple times"))
-                .emit());
-        }
-    }
-
-    Ok(())
-}
-
-/// Computes if an opaque type requires a lifetime parameter to be equal to
-/// another one or to the `'static` lifetime.
-/// These requirements are derived from the explicit and implied bounds.
-struct LazyOpaqueTyEnv<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    def_id: LocalDefId,
-
-    /// Equal parameters will have the same name. Computed Lazily.
-    /// Example:
-    ///     `type Opaque<'a: 'static, 'b: 'c, 'c: 'b> = impl Sized;`
-    ///     Identity args: `['a, 'b, 'c]`
-    ///     Canonical args: `['static, 'b, 'b]`
-    canonical_args: std::cell::OnceCell<ty::GenericArgsRef<'tcx>>,
-}
-
-impl<'tcx> LazyOpaqueTyEnv<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
-        Self { tcx, def_id, canonical_args: std::cell::OnceCell::new() }
-    }
-
-    fn param_equal_static(&self, param_index: usize) -> bool {
-        self.get_canonical_args()[param_index].expect_region().is_static()
-    }
-
-    fn params_equal(&self, param1: usize, param2: usize) -> bool {
-        let canonical_args = self.get_canonical_args();
-        canonical_args[param1] == canonical_args[param2]
-    }
-
-    fn param_is_error(&self, param_index: usize) -> Result<(), ErrorGuaranteed> {
-        self.get_canonical_args()[param_index].error_reported()
-    }
-
-    fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
-        use rustc_hir as hir;
-        use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-        use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
-
-        if let Some(&canonical_args) = self.canonical_args.get() {
-            return canonical_args;
-        }
-
-        let &Self { tcx, def_id, .. } = self;
-        let origin = tcx.local_opaque_ty_origin(def_id);
-        let parent = match origin {
-            hir::OpaqueTyOrigin::FnReturn { parent, .. }
-            | hir::OpaqueTyOrigin::AsyncFn { parent, .. }
-            | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent,
-        };
-        let param_env = tcx.param_env(parent);
-        let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
-            tcx,
-            def_id.to_def_id(),
-            |param, _| {
-                tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into()
-            },
-        );
-
-        // FIXME(#132279): It feels wrong to use `non_body_analysis` here given that we're
-        // in a body here.
-        let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
-        let ocx = ObligationCtxt::new(&infcx);
-
-        let wf_tys = ocx.assumed_wf_types(param_env, parent).unwrap_or_else(|_| {
-            tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
-            Default::default()
-        });
-        let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &wf_tys);
-        let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
-
-        let mut seen = vec![tcx.lifetimes.re_static];
-        let canonical_args = fold_regions(tcx, args, |r1, _| {
-            if r1.is_error() {
-                r1
-            } else if let Some(&r2) = seen.iter().find(|&&r2| {
-                let free_regions = outlives_env.free_region_map();
-                free_regions.sub_free_regions(tcx, r1, r2)
-                    && free_regions.sub_free_regions(tcx, r2, r1)
-            }) {
-                r2
-            } else {
-                seen.push(r1);
-                r1
-            }
-        });
-        self.canonical_args.set(canonical_args).unwrap();
-        canonical_args
-    }
-}
diff --git a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
index d0cfe572d087..b2ed8a358279 100644
--- a/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
+++ b/compiler/rustc_borrowck/src/region_infer/reverse_sccs.rs
@@ -20,10 +20,7 @@ pub(crate) struct ReverseSccGraph {
 
 impl ReverseSccGraph {
     /// Find all universal regions that are required to outlive the given SCC.
-    pub(super) fn upper_bounds<'a>(
-        &'a self,
-        scc0: ConstraintSccIndex,
-    ) -> impl Iterator<Item = RegionVid> + 'a {
+    pub(super) fn upper_bounds(&self, scc0: ConstraintSccIndex) -> impl Iterator<Item = RegionVid> {
         let mut duplicates = FxIndexSet::default();
         graph::depth_first_search(&self.graph, scc0)
             .flat_map(move |scc1| {
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index f1bcb353dc61..d9ac5b5cb132 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -88,7 +88,7 @@ impl LivenessValues {
     }
 
     /// Iterate through each region that has a value in this set.
-    pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> + '_ {
+    pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> {
         self.points.as_ref().expect("use with_specific_points").rows()
     }
 
@@ -96,7 +96,7 @@ impl LivenessValues {
     // We are passing query instability implications to the caller.
     #[rustc_lint_query_instability]
     #[allow(rustc::potential_query_instability)]
-    pub(crate) fn live_regions_unordered(&self) -> impl Iterator<Item = RegionVid> + '_ {
+    pub(crate) fn live_regions_unordered(&self) -> impl Iterator<Item = RegionVid> {
         self.live_regions.as_ref().unwrap().iter().copied()
     }
 
@@ -143,7 +143,7 @@ impl LivenessValues {
     }
 
     /// Returns an iterator of all the points where `region` is live.
-    fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> + '_ {
+    fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> {
         let Some(points) = &self.points else {
             unreachable!(
                 "Should be using LivenessValues::with_specific_points to ask whether live at a location"
@@ -340,7 +340,7 @@ impl<N: Idx> RegionValues<N> {
     }
 
     /// Returns the locations contained within a given region `r`.
-    pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a {
+    pub(crate) fn locations_outlived_by(&self, r: N) -> impl Iterator<Item = Location> {
         self.points.row(r).into_iter().flat_map(move |set| {
             set.iter()
                 .take_wh