about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-06-25 04:05:47 +0000
committerbors <bors@rust-lang.org>2025-06-25 04:05:47 +0000
commita17780db7b8eebbf42a1cbe6bc9cc83013820ba5 (patch)
treec5a5999a20a92ad660a65b0f5b24e6a0f2463c59
parent2c2bb995af398383e3b93b859302bdc447ca7a7c (diff)
parent2d73e6c6fe860b0e7db8bece4fb5318f176c7784 (diff)
downloadrust-a17780db7b8eebbf42a1cbe6bc9cc83013820ba5.tar.gz
rust-a17780db7b8eebbf42a1cbe6bc9cc83013820ba5.zip
Auto merge of #142997 - workingjubilee:rollup-6lxec87, r=workingjubilee
Rollup of 15 pull requests

Successful merges:

 - rust-lang/rust#135731 (Implement parsing of pinned borrows)
 - rust-lang/rust#138780 (Add `#[loop_match]` for improved DFA codegen)
 - rust-lang/rust#142453 (Windows: make `read_dir` stop iterating after the first error is encountered)
 - rust-lang/rust#142633 (Error on invalid signatures for interrupt ABIs)
 - rust-lang/rust#142768 (Avoid a bitcast FFI call in transmuting)
 - rust-lang/rust#142825 (Port `#[track_caller]` to the new attribute system)
 - rust-lang/rust#142844 (Enable short-ice for Windows)
 - rust-lang/rust#142934 (Tweak `-Zmacro-stats` measurement.)
 - rust-lang/rust#142955 (Couple of test suite fixes for cg_clif)
 - rust-lang/rust#142977 (rustdoc: Don't mark `#[target_feature]` functions as ⚠)
 - rust-lang/rust#142980 (Reduce mismatched-lifetime-syntaxes suggestions to MaybeIncorrect)
 - rust-lang/rust#142982 (Corrected spelling mistake in c_str.rs)
 - rust-lang/rust#142983 (Taint body on invalid call ABI)
 - rust-lang/rust#142988 (Update wasm-component-ld to 0.5.14)
 - rust-lang/rust#142993 (Update cargo)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock54
-rw-r--r--compiler/rustc_abi/src/extern_abi.rs5
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast_lowering/Cargo.toml1
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/stability.rs3
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml1
-rw-r--r--compiler/rustc_ast_passes/messages.ftl23
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs87
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs22
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs4
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs9
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs24
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/loop_match.rs31
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs8
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh14
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs54
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs10
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs12
-rw-r--r--compiler/rustc_expand/src/stats.rs28
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs13
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/check/entry.rs9
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs4
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl3
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/loops.rs70
-rw-r--r--compiler/rustc_interface/src/passes.rs4
-rw-r--r--compiler/rustc_lint/src/builtin.rs4
-rw-r--r--compiler/rustc_lint/src/lints.rs6
-rw-r--r--compiler/rustc_middle/src/thir.rs13
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs3
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs3
-rw-r--r--compiler/rustc_mir_build/Cargo.toml1
-rw-r--r--compiler/rustc_mir_build/messages.ftl33
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_place.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/category.rs2
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/into.rs122
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/stmt.rs3
-rw-r--r--compiler/rustc_mir_build/src/builder/matches/mod.rs144
-rw-r--r--compiler/rustc_mir_build/src/builder/scope.rs264
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs2
-rw-r--r--compiler/rustc_mir_build/src/errors.rs77
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs192
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs7
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs21
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs7
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs1
-rw-r--r--compiler/rustc_passes/messages.ftl9
-rw-r--r--compiler/rustc_passes/src/check_attr.rs42
-rw-r--r--compiler/rustc_passes/src/errors.rs18
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs3
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs1
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_target/src/spec/abi_map.rs3
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs8
-rw-r--r--library/core/src/ffi/c_str.rs2
-rw-r--r--library/std/src/sys/fs/windows.rs1
-rw-r--r--src/doc/unstable-book/src/compiler-flags/macro-stats.md6
-rw-r--r--src/doc/unstable-book/src/language-features/loop-match.md52
-rw-r--r--src/librustdoc/html/render/print_item.rs3
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs5
-rw-r--r--src/tools/rustfmt/src/expr.rs2
-rw-r--r--src/tools/rustfmt/tests/source/pin_sugar.rs10
-rw-r--r--src/tools/rustfmt/tests/target/pin_sugar.rs7
-rw-r--r--src/tools/tidy/src/deps.rs1
-rw-r--r--src/tools/wasm-component-ld/Cargo.toml2
-rw-r--r--tests/codegen/abi-x86-interrupt.rs6
-rw-r--r--tests/codegen/transmute-scalar.rs45
-rw-r--r--tests/pretty/pin-ergonomics-hir.pp44
-rw-r--r--tests/pretty/pin-ergonomics-hir.rs40
-rw-r--r--tests/pretty/pin-ergonomics.rs13
-rw-r--r--tests/run-make/bin-emit-no-symbols/app.rs10
-rw-r--r--tests/run-make/crate-circular-deps-link/a.rs8
-rw-r--r--tests/run-make/linker-warning/rmake.rs3
-rw-r--r--tests/run-make/no-alloc-shim/foo.rs8
-rw-r--r--tests/run-make/short-ice/rmake.rs15
-rw-r--r--tests/rustdoc/target-feature.rs38
-rw-r--r--tests/ui/abi/bad-custom.rs20
-rw-r--r--tests/ui/abi/bad-custom.stderr38
-rw-r--r--tests/ui/abi/cannot-be-called.avr.stderr24
-rw-r--r--tests/ui/abi/cannot-be-called.i686.stderr34
-rw-r--r--tests/ui/abi/cannot-be-called.msp430.stderr24
-rw-r--r--tests/ui/abi/cannot-be-called.riscv32.stderr38
-rw-r--r--tests/ui/abi/cannot-be-called.riscv64.stderr38
-rw-r--r--tests/ui/abi/cannot-be-called.rs19
-rw-r--r--tests/ui/abi/cannot-be-called.x64.stderr34
-rw-r--r--tests/ui/abi/cannot-be-called.x64_win.stderr34
-rw-r--r--tests/ui/abi/cannot-be-coroutine.avr.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.i686.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.msp430.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.riscv32.stderr35
-rw-r--r--tests/ui/abi/cannot-be-coroutine.riscv64.stderr35
-rw-r--r--tests/ui/abi/cannot-be-coroutine.rs54
-rw-r--r--tests/ui/abi/cannot-be-coroutine.x64.stderr23
-rw-r--r--tests/ui/abi/cannot-be-coroutine.x64_win.stderr23
-rw-r--r--tests/ui/abi/invalid-call-abi-ctfe.rs14
-rw-r--r--tests/ui/abi/invalid-call-abi-ctfe.stderr9
-rw-r--r--tests/ui/abi/invalid-call-abi.rs12
-rw-r--r--tests/ui/abi/invalid-call-abi.stderr9
-rw-r--r--tests/ui/allocator/no_std-alloc-error-handler-custom.rs13
-rw-r--r--tests/ui/allocator/no_std-alloc-error-handler-default.rs13
-rw-r--r--tests/ui/extern-flag/auxiliary/panic_handler.rs10
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.rs8
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.stderr8
-rw-r--r--tests/ui/feature-gates/feature-gate-loop-match.rs30
-rw-r--r--tests/ui/feature-gates/feature-gate-loop-match.stderr33
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.rs28
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr70
-rw-r--r--tests/ui/lint/unused/unused-attr-duplicate.stderr24
-rw-r--r--tests/ui/loop-match/break-to-block.rs23
-rw-r--r--tests/ui/loop-match/const-continue-to-block.rs26
-rw-r--r--tests/ui/loop-match/const-continue-to-block.stderr8
-rw-r--r--tests/ui/loop-match/const-continue-to-loop.rs27
-rw-r--r--tests/ui/loop-match/const-continue-to-loop.stderr8
-rw-r--r--tests/ui/loop-match/const-continue-to-polymorphic-const.rs29
-rw-r--r--tests/ui/loop-match/const-continue-to-polymorphic-const.stderr8
-rw-r--r--tests/ui/loop-match/drop-in-match-arm.rs47
-rw-r--r--tests/ui/loop-match/invalid-attribute.rs43
-rw-r--r--tests/ui/loop-match/invalid-attribute.stderr131
-rw-r--r--tests/ui/loop-match/invalid.rs161
-rw-r--r--tests/ui/loop-match/invalid.stderr91
-rw-r--r--tests/ui/loop-match/loop-match.rs45
-rw-r--r--tests/ui/loop-match/macro.rs48
-rw-r--r--tests/ui/loop-match/nested.rs83
-rw-r--r--tests/ui/loop-match/or-patterns.rs54
-rw-r--r--tests/ui/loop-match/unsupported-type.rs27
-rw-r--r--tests/ui/loop-match/unsupported-type.stderr10
-rw-r--r--tests/ui/loop-match/unwind.rs53
-rw-r--r--tests/ui/loop-match/valid-patterns.rs117
-rw-r--r--tests/ui/no_std/simple-runs.rs12
-rw-r--r--tests/ui/panic-runtime/incompatible-type.rs10
-rw-r--r--tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr238
-rw-r--r--tests/ui/pin-ergonomics/borrow-unpin.rs143
-rw-r--r--tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr136
-rw-r--r--tests/ui/pin-ergonomics/borrow.rs38
-rw-r--r--tests/ui/pin-ergonomics/coerce-non-pointer-pin.rs (renamed from tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.rs)0
-rw-r--r--tests/ui/pin-ergonomics/coerce-non-pointer-pin.stderr (renamed from tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-arg.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-arg.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-const-as-mut.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-const-as-mut.stderr (renamed from tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-once.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-once.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-once.stderr (renamed from tests/ui/async-await/pin-ergonomics/reborrow-once.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-self.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-self.rs)0
-rw-r--r--tests/ui/pin-ergonomics/reborrow-shorter.rs (renamed from tests/ui/async-await/pin-ergonomics/reborrow-shorter.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-ambiguity.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar-ambiguity.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-no-const.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar-no-const.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-no-const.stderr (renamed from tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr)0
-rw-r--r--tests/ui/pin-ergonomics/sugar-self.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar-self.rs)0
-rw-r--r--tests/ui/pin-ergonomics/sugar.rs (renamed from tests/ui/async-await/pin-ergonomics/sugar.rs)0
-rw-r--r--tests/ui/print-calling-conventions.stdout1
-rw-r--r--tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr8
-rw-r--r--tests/ui/stats/macro-stats.stderr40
-rw-r--r--tests/ui/thir-print/thir-tree-loop-match.rs22
-rw-r--r--tests/ui/thir-print/thir-tree-loop-match.stdout301
163 files changed, 4230 insertions, 430 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9a553ba48cf..b44dbd6ee60 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3282,6 +3282,7 @@ dependencies = [
  "rustc_abi",
  "rustc_ast",
  "rustc_ast_pretty",
+ "rustc_attr_data_structures",
  "rustc_attr_parsing",
  "rustc_data_structures",
  "rustc_errors",
@@ -3316,6 +3317,7 @@ dependencies = [
  "rustc_parse",
  "rustc_session",
  "rustc_span",
+ "rustc_target",
  "thin-vec",
 ]
 
@@ -4116,6 +4118,7 @@ dependencies = [
  "rustc_apfloat",
  "rustc_arena",
  "rustc_ast",
+ "rustc_attr_data_structures",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fluent_macro",
@@ -5796,9 +5799,9 @@ dependencies = [
 
 [[package]]
 name = "wasi-preview1-component-adapter-provider"
-version = "31.0.0"
+version = "34.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86fabda09a0d89ffd1615b297b4a5d4b4d99df9598aeb24685837e63019e927b"
+checksum = "aafa1e6af9a954a4bcf6ef420c33355d0ce84ddc6afbcba7bb6f05126f9120ae"
 
 [[package]]
 name = "wasm-bindgen"
@@ -5860,9 +5863,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-component-ld"
-version = "0.5.13"
+version = "0.5.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a60a07a994a3538b57d8c5f8caba19f4793fb4c7156276e5e90e90acbb829e20"
+checksum = "b015ec93764aa5517bc8b839efa9941b90be8ce680b1134f8224644ba1e48e3f"
 dependencies = [
  "anyhow",
  "clap",
@@ -5870,7 +5873,7 @@ dependencies = [
  "libc",
  "tempfile",
  "wasi-preview1-component-adapter-provider",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
  "wat",
  "windows-sys 0.59.0",
  "winsplit",
@@ -5897,12 +5900,12 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2"
+checksum = "170a0157eef517a179f2d20ed7c68df9c3f7f6c1c047782d488bf5a464174684"
 dependencies = [
  "leb128fmt",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
 ]
 
 [[package]]
@@ -5917,14 +5920,14 @@ dependencies = [
 
 [[package]]
 name = "wasm-metadata"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78fdb7d29a79191ab363dc90c1ddd3a1e880ffd5348d92d48482393a9e6c5f4d"
+checksum = "a42fe3f5cbfb56fc65311ef827930d06189160038e81db62188f66b4bf468e3a"
 dependencies = [
  "anyhow",
  "indexmap",
- "wasm-encoder 0.229.0",
- "wasmparser 0.229.0",
+ "wasm-encoder 0.234.0",
+ "wasmparser 0.234.0",
 ]
 
 [[package]]
@@ -5939,9 +5942,9 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c"
+checksum = "be22e5a8f600afce671dd53c8d2dd26b4b7aa810fd18ae27dfc49737f3e02fc5"
 dependencies = [
  "bitflags",
  "hashbrown",
@@ -5952,15 +5955,6 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.234.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be22e5a8f600afce671dd53c8d2dd26b4b7aa810fd18ae27dfc49737f3e02fc5"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "wasmparser"
 version = "0.235.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917"
@@ -6411,9 +6405,9 @@ dependencies = [
 
 [[package]]
 name = "wit-component"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f550067740e223bfe6c4878998e81cdbe2529dd9a793dc49248dd6613394e8b"
+checksum = "5a8888169acf4c6c4db535beb405b570eedac13215d6821ca9bd03190f7f8b8c"
 dependencies = [
  "anyhow",
  "bitflags",
@@ -6422,17 +6416,17 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.229.0",
+ "wasm-encoder 0.234.0",
  "wasm-metadata",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
  "wit-parser",
 ]
 
 [[package]]
 name = "wit-parser"
-version = "0.229.0"
+version = "0.234.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6"
+checksum = "465492df47d8dcc015a3b7f241aed8ea03688fee7c5e04162285c5b1a3539c8b"
 dependencies = [
  "anyhow",
  "id-arena",
@@ -6443,7 +6437,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.229.0",
+ "wasmparser 0.234.0",
 ]
 
 [[package]]
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 7457ae1f033..1b8c2de1588 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -36,6 +36,10 @@ pub enum ExternAbi {
     /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
     RustCold,
 
+    /// An always-invalid ABI that's used to test "this ABI is not supported by this platform"
+    /// in a platform-agnostic way.
+    RustInvalid,
+
     /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
     /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
     Unadjusted,
@@ -157,6 +161,7 @@ abi_impls! {
             RiscvInterruptS =><= "riscv-interrupt-s",
             RustCall =><= "rust-call",
             RustCold =><= "rust-cold",
+            RustInvalid =><= "rust-invalid",
             Stdcall { unwind: false } =><= "stdcall",
             Stdcall { unwind: true } =><= "stdcall-unwind",
             System { unwind: false } =><= "system",
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 11afd359e5a..ab8dac16026 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -904,6 +904,10 @@ pub enum BorrowKind {
     /// The resulting type is either `*const T` or `*mut T`
     /// where `T = typeof($expr)`.
     Raw,
+    /// A pinned borrow, `&pin const $expr` or `&pin mut $expr`.
+    /// The resulting type is either `Pin<&'a T>` or `Pin<&'a mut T>`
+    /// where `T = typeof($expr)` and `'a` is some lifetime.
+    Pin,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml
index 6ac258155fe..dc571f5c367 100644
--- a/compiler/rustc_ast_lowering/Cargo.toml
+++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
 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_attr_parsing = { path = "../rustc_attr_parsing" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index f297bf9f4cf..c2140514e31 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -4,6 +4,7 @@ use std::sync::Arc;
 use rustc_ast::ptr::P as AstP;
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::expr_to_string;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::HirId;
@@ -831,7 +832,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) {
         if self.tcx.features().async_fn_track_caller()
             && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
-            && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
+            && find_attr!(*attrs, AttributeKind::TrackCaller(_))
         {
             let unstable_span = self.mark_span_with_reason(
                 DesugaringKind::Async,
diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs
index b8fa2dd3dd6..9b60807e650 100644
--- a/compiler/rustc_ast_lowering/src/stability.rs
+++ b/compiler/rustc_ast_lowering/src/stability.rs
@@ -96,6 +96,9 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
         ExternAbi::RustCold => {
             Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental })
         }
+        ExternAbi::RustInvalid => {
+            Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail })
+        }
         ExternAbi::GpuKernel => Err(UnstableAbi {
             abi,
             feature: sym::abi_gpu_kernel,
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index c738cb2aa2f..1940628b44a 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -18,5 +18,6 @@ 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 9a267501230..6eddce7b590 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -1,20 +1,25 @@
-ast_passes_abi_custom_coroutine =
-    functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}`
+ast_passes_abi_cannot_be_coroutine =
+    functions with the {$abi} ABI cannot be `{$coroutine_kind_str}`
     .suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton
 
-ast_passes_abi_custom_invalid_signature =
-    invalid signature for `extern "custom"` function
-    .note = functions with the `"custom"` ABI cannot have any parameters or return type
-    .suggestion = remove the parameters and return type
-
 ast_passes_abi_custom_safe_foreign_function =
-    foreign functions with the `"custom"` ABI cannot be safe
+    foreign functions with the "custom" ABI cannot be safe
     .suggestion = remove the `safe` keyword from this definition
 
 ast_passes_abi_custom_safe_function =
-    functions with the `"custom"` ABI must be unsafe
+    functions with the "custom" ABI must be unsafe
     .suggestion = add the `unsafe` keyword to this definition
 
+ast_passes_abi_must_not_have_parameters_or_return_type=
+    invalid signature for `extern {$abi}` function
+    .note = functions with the {$abi} ABI cannot have any parameters or return type
+    .suggestion = remove the parameters and return type
+
+ast_passes_abi_must_not_have_return_type=
+    invalid signature for `extern {$abi}` function
+    .note = functions with the "custom" ABI cannot have a return type
+    .help = remove the return type
+
 ast_passes_assoc_const_without_body =
     associated constant in `impl` without body
     .suggestion = provide a definition for the constant
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index b69a91e2f5f..da248251203 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -21,7 +21,7 @@ use std::ops::{Deref, DerefMut};
 use std::str::FromStr;
 
 use itertools::{Either, Itertools};
-use rustc_abi::ExternAbi;
+use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
 use rustc_ast::*;
@@ -37,6 +37,7 @@ use rustc_session::lint::builtin::{
 };
 use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
 use rustc_span::{Ident, Span, kw, sym};
+use rustc_target::spec::{AbiMap, AbiMapping};
 use thin_vec::thin_vec;
 
 use crate::errors::{self, TildeConstReason};
@@ -365,31 +366,77 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    /// An `extern "custom"` function must be unsafe, and must not have any parameters or return
-    /// type.
-    fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
+    /// Check that the signature of this function does not violate the constraints of its ABI.
+    fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
+        match AbiMap::from_target(&self.sess.target).canonize_abi(abi, false) {
+            AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => {
+                match canon_abi {
+                    CanonAbi::C
+                    | CanonAbi::Rust
+                    | CanonAbi::RustCold
+                    | CanonAbi::Arm(_)
+                    | CanonAbi::GpuKernel
+                    | CanonAbi::X86(_) => { /* nothing to check */ }
+
+                    CanonAbi::Custom => {
+                        // An `extern "custom"` function must be unsafe.
+                        self.reject_safe_fn(abi, ctxt, sig);
+
+                        // An `extern "custom"` function cannot be `async` and/or `gen`.
+                        self.reject_coroutine(abi, sig);
+
+                        // An `extern "custom"` function must have type `fn()`.
+                        self.reject_params_or_return(abi, ident, sig);
+                    }
+
+                    CanonAbi::Interrupt(interrupt_kind) => {
+                        // An interrupt handler cannot be `async` and/or `gen`.
+                        self.reject_coroutine(abi, sig);
+
+                        if let InterruptKind::X86 = interrupt_kind {
+                            // "x86-interrupt" is special because it does have arguments.
+                            // FIXME(workingjubilee): properly lint on acceptable input types.
+                            if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
+                                self.dcx().emit_err(errors::AbiMustNotHaveReturnType {
+                                    span: ret_ty.span,
+                                    abi,
+                                });
+                            }
+                        } else {
+                            // An `extern "interrupt"` function must have type `fn()`.
+                            self.reject_params_or_return(abi, ident, sig);
+                        }
+                    }
+                }
+            }
+            AbiMapping::Invalid => { /* ignore */ }
+        }
+    }
+
+    fn reject_safe_fn(&self, abi: ExternAbi, ctxt: FnCtxt, sig: &FnSig) {
         let dcx = self.dcx();
 
-        // An `extern "custom"` function must be unsafe.
         match sig.header.safety {
             Safety::Unsafe(_) => { /* all good */ }
             Safety::Safe(safe_span) => {
-                let safe_span =
-                    self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span));
+                let source_map = self.sess.psess.source_map();
+                let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span));
                 dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
             }
             Safety::Default => match ctxt {
                 FnCtxt::Foreign => { /* all good */ }
                 FnCtxt::Free | FnCtxt::Assoc(_) => {
-                    self.dcx().emit_err(errors::AbiCustomSafeFunction {
+                    dcx.emit_err(errors::AbiCustomSafeFunction {
                         span: sig.span,
+                        abi,
                         unsafe_span: sig.span.shrink_to_lo(),
                     });
                 }
             },
         }
+    }
 
-        // An `extern "custom"` function cannot be `async` and/or `gen`.
+    fn reject_coroutine(&self, abi: ExternAbi, sig: &FnSig) {
         if let Some(coroutine_kind) = sig.header.coroutine_kind {
             let coroutine_kind_span = self
                 .sess
@@ -397,14 +444,16 @@ impl<'a> AstValidator<'a> {
                 .source_map()
                 .span_until_non_whitespace(coroutine_kind.span().to(sig.span));
 
-            self.dcx().emit_err(errors::AbiCustomCoroutine {
+            self.dcx().emit_err(errors::AbiCannotBeCoroutine {
                 span: sig.span,
+                abi,
                 coroutine_kind_span,
                 coroutine_kind_str: coroutine_kind.as_str(),
             });
         }
+    }
 
-        // An `extern "custom"` function must not have any parameters or return type.
+    fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) {
         let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
         if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
             spans.push(ret_ty.span);
@@ -415,11 +464,12 @@ impl<'a> AstValidator<'a> {
             let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
             let padding = if header_span.is_empty() { "" } else { " " };
 
-            self.dcx().emit_err(errors::AbiCustomInvalidSignature {
+            self.dcx().emit_err(errors::AbiMustNotHaveParametersOrReturnType {
                 spans,
                 symbol: ident.name,
                 suggestion_span,
                 padding,
+                abi,
             });
         }
     }
@@ -1199,9 +1249,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.check_foreign_fn_bodyless(*ident, body.as_deref());
                 self.check_foreign_fn_headerless(sig.header);
                 self.check_foreign_item_ascii_only(*ident);
-                if self.extern_mod_abi == Some(ExternAbi::Custom) {
-                    self.check_custom_abi(FnCtxt::Foreign, ident, sig);
-                }
+                self.check_extern_fn_signature(
+                    self.extern_mod_abi.unwrap_or(ExternAbi::FALLBACK),
+                    FnCtxt::Foreign,
+                    ident,
+                    sig,
+                );
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
@@ -1411,9 +1464,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
 
         if let FnKind::Fn(ctxt, _, fun) = fk
             && let Extern::Explicit(str_lit, _) = fun.sig.header.ext
-            && let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
+            && let Ok(abi) = ExternAbi::from_str(str_lit.symbol.as_str())
         {
-            self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
+            self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig);
         }
 
         self.check_c_variadic_type(fk);
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index c437e62f4d3..d387a4a310e 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -1,5 +1,6 @@
 //! Errors emitted by ast_passes.
 
+use rustc_abi::ExternAbi;
 use rustc_ast::ParamKindOrd;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
@@ -845,6 +846,7 @@ pub(crate) struct AbiCustomSafeForeignFunction {
 pub(crate) struct AbiCustomSafeFunction {
     #[primary_span]
     pub span: Span,
+    pub abi: ExternAbi,
 
     #[suggestion(
         ast_passes_suggestion,
@@ -856,10 +858,11 @@ pub(crate) struct AbiCustomSafeFunction {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_abi_custom_coroutine)]
-pub(crate) struct AbiCustomCoroutine {
+#[diag(ast_passes_abi_cannot_be_coroutine)]
+pub(crate) struct AbiCannotBeCoroutine {
     #[primary_span]
     pub span: Span,
+    pub abi: ExternAbi,
 
     #[suggestion(
         ast_passes_suggestion,
@@ -872,11 +875,12 @@ pub(crate) struct AbiCustomCoroutine {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_abi_custom_invalid_signature)]
+#[diag(ast_passes_abi_must_not_have_parameters_or_return_type)]
 #[note]
-pub(crate) struct AbiCustomInvalidSignature {
+pub(crate) struct AbiMustNotHaveParametersOrReturnType {
     #[primary_span]
     pub spans: Vec<Span>,
+    pub abi: ExternAbi,
 
     #[suggestion(
         ast_passes_suggestion,
@@ -888,3 +892,13 @@ pub(crate) struct AbiCustomInvalidSignature {
     pub symbol: Symbol,
     pub padding: &'static str,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_must_not_have_return_type)]
+#[note]
+pub(crate) struct AbiMustNotHaveReturnType {
+    #[primary_span]
+    #[help]
+    pub span: Span,
+    pub abi: ExternAbi,
+}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index f6b5ff404db..7651e8365a2 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -357,6 +357,10 @@ impl<'a> State<'a> {
                 self.word_nbsp("raw");
                 self.print_mutability(mutability, true);
             }
+            ast::BorrowKind::Pin => {
+                self.word_nbsp("pin");
+                self.print_mutability(mutability, true);
+            }
         }
         self.print_expr_cond_paren(
             expr,
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index ae3a1a41387..dc3598bcc36 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -212,6 +212,9 @@ pub enum AttributeKind {
         first_span: Span,
     },
 
+    /// Represents `#[const_continue]`.
+    ConstContinue(Span),
+
     /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
     ConstStability {
         stability: PartialConstStability,
@@ -231,6 +234,9 @@ pub enum AttributeKind {
     /// Represents `#[inline]` and `#[rustc_force_inline]`.
     Inline(InlineAttr, Span),
 
+    /// Represents `#[loop_match]`.
+    LoopMatch(Span),
+
     /// Represents `#[rustc_macro_transparency]`.
     MacroTransparency(Transparency),
 
@@ -268,5 +274,8 @@ pub enum AttributeKind {
         /// Span of the attribute.
         span: Span,
     },
+
+    /// Represents `#[track_caller]`
+    TrackCaller(Span),
     // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 0fa69c40154..eadf8657a0f 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -1,7 +1,7 @@
 use rustc_attr_data_structures::{AttributeKind, OptimizeAttr};
 use rustc_feature::{AttributeTemplate, template};
 use rustc_session::parse::feature_err;
-use rustc_span::{Span, sym};
+use rustc_span::{Span, Symbol, sym};
 
 use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
 use crate::context::{AcceptContext, FinalizeContext, Stage};
@@ -11,7 +11,7 @@ use crate::session_diagnostics::NakedFunctionIncompatibleAttribute;
 pub(crate) struct OptimizeParser;
 
 impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
-    const PATH: &[rustc_span::Symbol] = &[sym::optimize];
+    const PATH: &[Symbol] = &[sym::optimize];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
     const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
@@ -44,7 +44,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
 pub(crate) struct ColdParser;
 
 impl<S: Stage> SingleAttributeParser<S> for ColdParser {
-    const PATH: &[rustc_span::Symbol] = &[sym::cold];
+    const PATH: &[Symbol] = &[sym::cold];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
     const TEMPLATE: AttributeTemplate = template!(Word);
@@ -166,6 +166,24 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
     }
 }
 
+pub(crate) struct TrackCallerParser;
+
+impl<S: Stage> SingleAttributeParser<S> for TrackCallerParser {
+    const PATH: &[Symbol] = &[sym::track_caller];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+            return None;
+        }
+
+        Some(AttributeKind::TrackCaller(cx.attr_span))
+    }
+}
+
 pub(crate) struct NoMangleParser;
 
 impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
new file mode 100644
index 00000000000..f6c7ac5e3a3
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
@@ -0,0 +1,31 @@
+use rustc_attr_data_structures::AttributeKind;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct LoopMatchParser;
+impl<S: Stage> SingleAttributeParser<S> for LoopMatchParser {
+    const PATH: &[Symbol] = &[sym::loop_match];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::LoopMatch(cx.attr_span))
+    }
+}
+
+pub(crate) struct ConstContinueParser;
+impl<S: Stage> SingleAttributeParser<S> for ConstContinueParser {
+    const PATH: &[Symbol] = &[sym::const_continue];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
+    const TEMPLATE: AttributeTemplate = template!(Word);
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+        Some(AttributeKind::ConstContinue(cx.attr_span))
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index ac7e90fd902..d407669cb41 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -32,6 +32,7 @@ pub(crate) mod confusables;
 pub(crate) mod deprecation;
 pub(crate) mod inline;
 pub(crate) mod lint_helpers;
+pub(crate) mod loop_match;
 pub(crate) mod must_use;
 pub(crate) mod repr;
 pub(crate) mod semantics;
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 43b2d07dbe1..6ca5c64e0bc 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -15,11 +15,14 @@ use rustc_session::Session;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 
 use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
-use crate::attributes::codegen_attrs::{ColdParser, NakedParser, NoMangleParser, OptimizeParser};
+use crate::attributes::codegen_attrs::{
+    ColdParser, NakedParser, NoMangleParser, OptimizeParser, TrackCallerParser,
+};
 use crate::attributes::confusables::ConfusablesParser;
 use crate::attributes::deprecation::DeprecationParser;
 use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
 use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
+use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
 use crate::attributes::must_use::MustUseParser;
 use crate::attributes::repr::{AlignParser, ReprParser};
 use crate::attributes::semantics::MayDangleParser;
@@ -111,9 +114,11 @@ attribute_parsers!(
         // tidy-alphabetical-start
         Single<AsPtrParser>,
         Single<ColdParser>,
+        Single<ConstContinueParser>,
         Single<ConstStabilityIndirectParser>,
         Single<DeprecationParser>,
         Single<InlineParser>,
+        Single<LoopMatchParser>,
         Single<MayDangleParser>,
         Single<MustUseParser>,
         Single<NoMangleParser>,
@@ -121,6 +126,7 @@ attribute_parsers!(
         Single<PubTransparentParser>,
         Single<RustcForceInlineParser>,
         Single<SkipDuringMethodDispatchParser>,
+        Single<TrackCallerParser>,
         Single<TransparencyParser>,
         // tidy-alphabetical-end
     ];
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index 32c71f433b0..7e356b4b462 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -151,20 +151,6 @@ rm tests/ui/process/process-panic-after-fork.rs # same
 cp ../dist/bin/rustdoc-clif ../dist/bin/rustdoc # some tests expect bin/rustdoc to exist
 
 cat <<EOF | git apply -
-diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
-index 30387af428c..f7895b12961 100644
---- a/tests/run-make/linker-warning/rmake.rs
-+++ b/tests/run-make/linker-warning/rmake.rs
-@@ -57,7 +57,8 @@ fn main() {
-             .actual_text("(linker error)", out.stderr())
--            .normalize(r#"/rustc[^/]*/"#, "/rustc/")
-+            .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/")
-+            .normalize("libpanic_abort", "libpanic_unwind")
-             .normalize(
-                 regex::escape(run_make_support::build_root().to_str().unwrap()),
-                 "/build-root",
-             )
-             .normalize(r#""[^"]*\/symbols.o""#, "\\"/symbols.o\\"")
 diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
 index 073116933bd..c3e4578204d 100644
 --- a/src/tools/compiletest/src/runtest/run_make.rs
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index f769b393528..7bd27eb3ef1 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -95,17 +95,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         // In these cases, we bail from performing further checks that are only meaningful for
         // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
         // report a delayed bug, just in case `check_attr` isn't doing its job.
-        let fn_sig = || {
+        let fn_sig = |attr_span| {
             use DefKind::*;
 
             let def_kind = tcx.def_kind(did);
             if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
                 Some(tcx.fn_sig(did))
             } else {
-                tcx.dcx().span_delayed_bug(
-                    attr.span(),
-                    "this attribute can only be applied to functions",
-                );
+                tcx.dcx()
+                    .span_delayed_bug(attr_span, "this attribute can only be applied to functions");
                 None
             }
         };
@@ -142,6 +140,29 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         });
                     }
                 }
+                AttributeKind::TrackCaller(attr_span) => {
+                    let is_closure = tcx.is_closure_like(did.to_def_id());
+
+                    if !is_closure
+                        && let Some(fn_sig) = fn_sig(*attr_span)
+                        && fn_sig.skip_binder().abi() != ExternAbi::Rust
+                    {
+                        tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span });
+                    }
+                    if is_closure
+                        && !tcx.features().closure_track_caller()
+                        && !attr_span.allows_unstable(sym::closure_track_caller)
+                    {
+                        feature_err(
+                            &tcx.sess,
+                            sym::closure_track_caller,
+                            *attr_span,
+                            "`#[track_caller]` on closures is currently unstable",
+                        )
+                        .emit();
+                    }
+                    codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
+                }
                 _ => {}
             }
         }
@@ -202,29 +223,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 }
             }
             sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
-            sym::track_caller => {
-                let is_closure = tcx.is_closure_like(did.to_def_id());
-
-                if !is_closure
-                    && let Some(fn_sig) = fn_sig()
-                    && fn_sig.skip_binder().abi() != ExternAbi::Rust
-                {
-                    tcx.dcx().emit_err(errors::RequiresRustAbi { span: attr.span() });
-                }
-                if is_closure
-                    && !tcx.features().closure_track_caller()
-                    && !attr.span().allows_unstable(sym::closure_track_caller)
-                {
-                    feature_err(
-                        &tcx.sess,
-                        sym::closure_track_caller,
-                        attr.span(),
-                        "`#[track_caller]` on closures is currently unstable",
-                    )
-                    .emit();
-                }
-                codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER
-            }
             sym::export_name => {
                 if let Some(s) = attr.value_str() {
                     if s.as_str().contains('\0') {
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index e1d8b7546cf..db5ac6a514f 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -1123,7 +1123,7 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     // While optimizations will remove no-op transmutes, they might still be
     // there in debug or things that aren't no-op in MIR because they change
     // the Rust type but not the underlying layout/niche.
-    if from_scalar == to_scalar && from_backend_ty == to_backend_ty {
+    if from_scalar == to_scalar {
         return imm;
     }
 
@@ -1142,7 +1142,13 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
 
     imm = match (from_scalar.primitive(), to_scalar.primitive()) {
-        (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
+        (Int(..) | Float(_), Int(..) | Float(_)) => {
+            if from_backend_ty == to_backend_ty {
+                imm
+            } else {
+                bx.bitcast(imm, to_backend_ty)
+            }
+        }
         (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
         (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm),
         (Pointer(..), Int(..)) => {
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 887275e7294..f5b7a6066c8 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -600,11 +600,13 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
                 kind: ccx.const_kind(),
                 teach: ccx.tcx.sess.teach(E0764),
             }),
-            hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
-                span,
-                kind: ccx.const_kind(),
-                teach: ccx.tcx.sess.teach(E0764),
-            }),
+            hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
+                ccx.dcx().create_err(errors::MutableRefEscaping {
+                    span,
+                    kind: ccx.const_kind(),
+                    teach: ccx.tcx.sess.teach(E0764),
+                })
+            }
         }
     }
 }
diff --git a/compiler/rustc_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs
index 6b2ad30dffd..b4c4eac028f 100644
--- a/compiler/rustc_expand/src/stats.rs
+++ b/compiler/rustc_expand/src/stats.rs
@@ -15,15 +15,11 @@ pub struct MacroStat {
     /// Number of uses of the macro.
     pub uses: usize,
 
-    /// Net increase in number of lines of code (when pretty-printed), i.e.
-    /// `lines(output) - lines(invocation)`. Can be negative because a macro
-    /// output may be smaller than the invocation.
-    pub lines: isize,
-
-    /// Net increase in number of lines of code (when pretty-printed), i.e.
-    /// `bytes(output) - bytes(invocation)`. Can be negative because a macro
-    /// output may be smaller than the invocation.
-    pub bytes: isize,
+    /// Number of lines of code (when pretty-printed).
+    pub lines: usize,
+
+    /// Number of bytes of code (when pretty-printed).
+    pub bytes: usize,
 }
 
 pub(crate) fn elems_to_string<T>(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> String) -> String {
@@ -131,16 +127,12 @@ pub(crate) fn update_macro_stats(
     input: &str,
     fragment: &AstFragment,
 ) {
-    fn lines_and_bytes(s: &str) -> (usize, usize) {
-        (s.trim_end().split('\n').count(), s.len())
-    }
-
     // Measure the size of the output by pretty-printing it and counting
     // the lines and bytes.
     let name = Symbol::intern(&pprust::path_to_string(path));
     let output = fragment.to_string();
-    let (in_l, in_b) = lines_and_bytes(input);
-    let (out_l, out_b) = lines_and_bytes(&output);
+    let num_lines = output.trim_end().split('\n').count();
+    let num_bytes = output.len();
 
     // This code is useful for debugging `-Zmacro-stats`. For every
     // invocation it prints the full input and output.
@@ -157,7 +149,7 @@ pub(crate) fn update_macro_stats(
             {name}: [{crate_name}] ({fragment_kind:?}) {span}\n\
             -------------------------------\n\
             {input}\n\
-            -- ({in_l} lines, {in_b} bytes) --> ({out_l} lines, {out_b} bytes) --\n\
+            -- {num_lines} lines, {num_bytes} bytes --\n\
             {output}\n\
         "
         );
@@ -166,6 +158,6 @@ pub(crate) fn update_macro_stats(
     // The recorded size is the difference between the input and the output.
     let entry = ecx.macro_stats.entry((name, macro_kind)).or_insert(MacroStat::default());
     entry.uses += 1;
-    entry.lines += out_l as isize - in_l as isize;
-    entry.bytes += out_b as isize - in_b as isize;
+    entry.lines += num_lines;
+    entry.bytes += num_bytes;
 }
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 3540c9b4bf0..8c0f279e343 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -657,6 +657,19 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const),
     ),
 
+    // The `#[loop_match]` and `#[const_continue]` attributes are part of the
+    // lang experiment for RFC 3720 tracked in:
+    //
+    // - https://github.com/rust-lang/rust/issues/132306
+    gated!(
+        const_continue, Normal, template!(Word), ErrorFollowing,
+        EncodeCrossCrate::No, loop_match, experimental!(const_continue)
+    ),
+    gated!(
+        loop_match, Normal, template!(Word), ErrorFollowing,
+        EncodeCrossCrate::No, loop_match, experimental!(loop_match)
+    ),
+
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
     // ==========================================================================
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 91715851226..d9d5334615a 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -557,6 +557,8 @@ declare_features! (
     /// Allows using `#[link(kind = "link-arg", name = "...")]`
     /// to pass custom arguments to the linker.
     (unstable, link_arg_attribute, "1.76.0", Some(99427)),
+    /// Allows fused `loop`/`match` for direct intraprocedural jumps.
+    (incomplete, loop_match, "CURRENT_RUSTC_VERSION", Some(132306)),
     /// Give access to additional metadata about declarative macro meta-variables.
     (unstable, macro_metavar_expr, "1.61.0", Some(83527)),
     /// Provides a way to concatenate identifiers using metavariable expressions.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index bd2252c1bf8..8c43d10b9e4 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -1,5 +1,5 @@
 hir_analysis_abi_custom_clothed_function =
-    items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    items with the "custom" ABI can only be declared externally or defined via naked functions
     .suggestion = convert this to an `#[unsafe(naked)]` function
 
 hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`
diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs
index 3bad36da999..b556683e80a 100644
--- a/compiler/rustc_hir_analysis/src/check/entry.rs
+++ b/compiler/rustc_hir_analysis/src/check/entry.rs
@@ -1,14 +1,15 @@
 use std::ops::Not;
 
 use rustc_abi::ExternAbi;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_hir as hir;
 use rustc_hir::Node;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, TyCtxt, TypingMode};
 use rustc_session::config::EntryFnType;
+use rustc_span::Span;
 use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
-use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
 
@@ -98,8 +99,10 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         error = true;
     }
 
-    for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
-        tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span });
+    if let Some(attr_span) =
+        find_attr!(tcx.get_all_attrs(main_def_id), AttributeKind::TrackCaller(span) => *span)
+    {
+        tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span });
         error = true;
     }
 
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index d3289e4cc6d..ff87d3dec6e 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1335,6 +1335,10 @@ impl<'a> State<'a> {
                 self.word_nbsp("raw");
                 self.print_mutability(mutability, true);
             }
+            hir::BorrowKind::Pin => {
+                self.word_nbsp("pin");
+                self.print_mutability(mutability, true);
+            }
         }
         self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Prefix);
     }
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 258535f3742..c21b16c9f9f 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -79,6 +79,9 @@ hir_typeck_cast_unknown_pointer = cannot cast {$to ->
     .note = the type information given here is insufficient to check whether the pointer cast is valid
     .label_from = the type information given here is insufficient to check whether the pointer cast is valid
 
+hir_typeck_const_continue_bad_label =
+    `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+
 hir_typeck_const_select_must_be_const = this argument must be a `const fn`
     .help = consult the documentation on `const_eval_select` for more information
 
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 7a3647df0c4..f790c51f8f1 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -156,7 +156,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(crate) fn check_call_abi(&self, abi: ExternAbi, span: Span) {
         let canon_abi = match AbiMap::from_target(&self.sess().target).canonize_abi(abi, false) {
             AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => canon_abi,
-            AbiMapping::Invalid => return,
+            AbiMapping::Invalid => {
+                // This should be reported elsewhere, but we want to taint this body
+                // so that we don't try to evaluate calls to ABIs that are invalid.
+                let guar = self.dcx().span_delayed_bug(
+                    span,
+                    format!("invalid abi for platform should have reported an error: {abi}"),
+                );
+                self.set_tainted_by_errors(guar);
+                return;
+            }
         };
 
         let valid = match canon_abi {
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 5fea0c62843..3606c778fc4 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -1167,3 +1167,10 @@ pub(crate) struct AbiCannotBeCalled {
     pub span: Span,
     pub abi: ExternAbi,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_const_continue_bad_label)]
+pub(crate) struct ConstContinueBadLabel {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 672f3bc67ce..2bc9dadb665 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -690,7 +690,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_named_place_expr(oprnd);
                 Ty::new_ptr(self.tcx, ty, mutbl)
             }
-            hir::BorrowKind::Ref => {
+            hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
                 // Note: at this point, we cannot say what the best lifetime
                 // is to use for resulting pointer. We want to use the
                 // shortest lifetime possible so as to avoid spurious borrowck
@@ -706,7 +706,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // whose address was taken can actually be made to live as long
                 // as it needs to live.
                 let region = self.next_region_var(infer::BorrowRegion(expr.span));
-                Ty::new_ref(self.tcx, region, ty, mutbl)
+                match kind {
+                    hir::BorrowKind::Ref => Ty::new_ref(self.tcx, region, ty, mutbl),
+                    hir::BorrowKind::Pin => Ty::new_pinned_ref(self.tcx, region, ty, mutbl),
+                    _ => unreachable!(),
+                }
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs
index b06e0704b6f..80eab578f13 100644
--- a/compiler/rustc_hir_typeck/src/loops.rs
+++ b/compiler/rustc_hir_typeck/src/loops.rs
@@ -2,6 +2,8 @@ use std::collections::BTreeMap;
 use std::fmt;
 
 use Context::*;
+use rustc_ast::Label;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
@@ -14,8 +16,9 @@ use rustc_span::hygiene::DesugaringKind;
 use rustc_span::{BytePos, Span};
 
 use crate::errors::{
-    BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
-    OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
+    BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ConstContinueBadLabel,
+    ContinueLabeledBlock, OutsideLoop, OutsideLoopSuggestion, UnlabeledCfInWhileCondition,
+    UnlabeledInLabeledBlock,
 };
 
 /// The context in which a block is encountered.
@@ -37,6 +40,11 @@ enum Context {
     AnonConst,
     /// E.g. `const { ... }`.
     ConstBlock,
+    /// E.g. `#[loop_match] loop { state = 'label: { /* ... */ } }`.
+    LoopMatch {
+        /// The label of the labeled block (not of the loop itself).
+        labeled_block: Label,
+    },
 }
 
 #[derive(Clone)]
@@ -141,7 +149,12 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
                 }
             }
             hir::ExprKind::Loop(ref b, _, source, _) => {
-                self.with_context(Loop(source), |v| v.visit_block(b));
+                let cx = match self.is_loop_match(e, b) {
+                    Some(labeled_block) => LoopMatch { labeled_block },
+                    None => Loop(source),
+                };
+
+                self.with_context(cx, |v| v.visit_block(b));
             }
             hir::ExprKind::Closure(&hir::Closure {
                 ref fn_decl, body, fn_decl_span, kind, ..
@@ -197,6 +210,23 @@ impl<'hir> Visitor<'hir> for CheckLoopVisitor<'hir> {
                     Err(hir::LoopIdError::UnresolvedLabel) => None,
                 };
 
+                // A `#[const_continue]` must break to a block in a `#[loop_match]`.
+                if find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::ConstContinue(_)) {
+                    if let Some(break_label) = break_label.label {
+                        let is_target_label = |cx: &Context| match cx {
+                            Context::LoopMatch { labeled_block } => {
+                                break_label.ident.name == labeled_block.ident.name
+                            }
+                            _ => false,
+                        };
+
+                        if !self.cx_stack.iter().rev().any(is_target_label) {
+                            let span = break_label.ident.span;
+                            self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
+                        }
+                    }
+                }
+
                 if let Some(Node::Block(_)) = loop_id.map(|id| self.tcx.hir_node(id)) {
                     return;
                 }
@@ -299,7 +329,7 @@ impl<'hir> CheckLoopVisitor<'hir> {
         cx_pos: usize,
     ) {
         match self.cx_stack[cx_pos] {
-            LabeledBlock | Loop(_) => {}
+            LabeledBlock | Loop(_) | LoopMatch { .. } => {}
             Closure(closure_span) => {
                 self.tcx.dcx().emit_err(BreakInsideClosure {
                     span,
@@ -380,4 +410,36 @@ impl<'hir> CheckLoopVisitor<'hir> {
             });
         }
     }
+
+    /// Is this a loop annotated with `#[loop_match]` that looks syntactically sound?
+    fn is_loop_match(
+        &self,
+        e: &'hir hir::Expr<'hir>,
+        body: &'hir hir::Block<'hir>,
+    ) -> Option<Label> {
+        if !find_attr!(self.tcx.hir_attrs(e.hir_id), AttributeKind::LoopMatch(_)) {
+            return None;
+        }
+
+        // NOTE: Diagnostics are emitted during MIR construction.
+
+        // Accept either `state = expr` or `state = expr;`.
+        let loop_body_expr = match body.stmts {
+            [] => match body.expr {
+                Some(expr) => expr,
+                None => return None,
+            },
+            [single] if body.expr.is_none() => match single.kind {
+                hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
+                _ => return None,
+            },
+            [..] => return None,
+        };
+
+        let hir::ExprKind::Assign(_, rhs_expr, _) = loop_body_expr.kind else { return None };
+
+        let hir::ExprKind::Block(_, label) = rhs_expr.kind else { return None };
+
+        label
+    }
 }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index e8c23ef997c..edfb05e2ccd 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -355,9 +355,9 @@ fn print_macro_stats(ecx: &ExtCtxt<'_>) {
             "{prefix} {:<name_w$}{:>uses_w$}{:>lines_w$}{:>avg_lines_w$}{:>bytes_w$}{:>avg_bytes_w$}",
             name,
             thousands::usize_with_underscores(uses),
-            thousands::isize_with_underscores(lines),
+            thousands::usize_with_underscores(lines),
             thousands::f64p1_with_underscores(avg_lines),
-            thousands::isize_with_underscores(bytes),
+            thousands::usize_with_underscores(bytes),
             thousands::f64p1_with_underscores(avg_bytes),
         );
     }
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index ac405277c4e..5bbe69c8d65 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1184,11 +1184,11 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
         if fn_kind.asyncness().is_async()
             && !cx.tcx.features().async_fn_track_caller()
             // Now, check if the function has the `#[track_caller]` attribute
-            && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
+            && let Some(attr_span) = find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::TrackCaller(span) => *span)
         {
             cx.emit_span_lint(
                 UNGATED_ASYNC_FN_TRACK_CALLER,
-                attr.span(),
+                attr_span,
                 BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess },
             );
         }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 4d405d915fb..eedb897312e 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -3278,7 +3278,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
                 diag.multipart_suggestion_with_style(
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit,
                     suggestions,
-                    Applicability::MachineApplicable,
+                    Applicability::MaybeIncorrect,
                     style(tool_only),
                 );
             }
@@ -3293,7 +3293,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
                 diag.multipart_suggestion_with_style(
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed,
                     suggestions,
-                    Applicability::MachineApplicable,
+                    Applicability::MaybeIncorrect,
                     style(tool_only),
                 );
             }
@@ -3308,7 +3308,7 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
                 diag.multipart_suggestion_with_style(
                     msg,
                     suggestions,
-                    Applicability::MachineApplicable,
+                    Applicability::MaybeIncorrect,
                     style(tool_only),
                 );
             }
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index b9a014d14c0..d0e72a86d8a 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -378,6 +378,14 @@ pub enum ExprKind<'tcx> {
     Loop {
         body: ExprId,
     },
+    /// A `#[loop_match] loop { state = 'blk: { match state { ... } } }` expression.
+    LoopMatch {
+        /// The state variable that is updated, and also the scrutinee of the match.
+        state: ExprId,
+        region_scope: region::Scope,
+        arms: Box<[ArmId]>,
+        match_span: Span,
+    },
     /// Special expression representing the `let` part of an `if let` or similar construct
     /// (including `if let` guards in match arms, and let-chains formed by `&&`).
     ///
@@ -454,6 +462,11 @@ pub enum ExprKind<'tcx> {
     Continue {
         label: region::Scope,
     },
+    /// A `#[const_continue] break` expression.
+    ConstContinue {
+        label: region::Scope,
+        value: ExprId,
+    },
     /// A `return` expression.
     Return {
         value: Option<ExprId>,
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index d8743814d79..c9ef723aea4 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -83,7 +83,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
             visitor.visit_pat(pat);
         }
         Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
-        Match { scrutinee, ref arms, .. } => {
+        LoopMatch { state: scrutinee, ref arms, .. } | Match { scrutinee, ref arms, .. } => {
             visitor.visit_expr(&visitor.thir()[scrutinee]);
             for &arm in &**arms {
                 visitor.visit_arm(&visitor.thir()[arm]);
@@ -108,6 +108,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
             }
         }
         Continue { label: _ } => {}
+        ConstContinue { value, label: _ } => visitor.visit_expr(&visitor.thir()[value]),
         Return { value } => {
             if let Some(value) = value {
                 visitor.visit_expr(&visitor.thir()[value])
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 13c281a6182..5cb943b0d8c 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1253,7 +1253,8 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
         | CCmseNonSecureCall
         | CCmseNonSecureEntry
         | Custom
-        | Unadjusted => false,
+        | Unadjusted
+        | RustInvalid => false,
         Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
     }
 }
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
index 1534865cdd3..c4c2d8a7ac8 100644
--- a/compiler/rustc_mir_build/Cargo.toml
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -11,6 +11,7 @@ rustc_abi = { path = "../rustc_abi" }
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
+rustc_attr_data_structures = { path = "../rustc_attr_data_structures" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index fae159103e7..e339520cd86 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -84,6 +84,15 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
 
 mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
 
+mir_build_const_continue_bad_const = could not determine the target branch for this `#[const_continue]`
+    .label = this value is too generic
+    .note = the value must be a literal or a monomorphic const
+
+mir_build_const_continue_missing_value = a `#[const_continue]` must break to a label with a value
+
+mir_build_const_continue_unknown_jump_target = the target of this `#[const_continue]` is not statically known
+    .label = this value must be a literal or a monomorphic const
+
 mir_build_const_defined_here = constant defined here
 
 mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
@@ -212,6 +221,30 @@ mir_build_literal_in_range_out_of_bounds =
     literal out of range for `{$ty}`
     .label = this value does not fit into the type `{$ty}` whose range is `{$min}..={$max}`
 
+mir_build_loop_match_arm_with_guard =
+    match arms that are part of a `#[loop_match]` cannot have guards
+
+mir_build_loop_match_bad_rhs =
+    this expression must be a single `match` wrapped in a labeled block
+
+mir_build_loop_match_bad_statements =
+    statements are not allowed in this position within a `#[loop_match]`
+
+mir_build_loop_match_invalid_match =
+    invalid match on `#[loop_match]` state
+    .note = a local variable must be the scrutinee within a `#[loop_match]`
+
+mir_build_loop_match_invalid_update =
+    invalid update of the `#[loop_match]` state
+    .label = the assignment must update this variable
+
+mir_build_loop_match_missing_assignment =
+    expected a single assignment expression
+
+mir_build_loop_match_unsupported_type =
+    this `#[loop_match]` state value has type `{$ty}`, which is not supported
+    .note = only integers, floats, bool, char, and enums without fields are supported
+
 mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
     lower range bound must be less than or equal to upper
     .label = lower bound larger than upper bound
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
index f8c64d7d13e..99148504a87 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs
@@ -565,12 +565,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | ExprKind::Match { .. }
             | ExprKind::If { .. }
             | ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::Block { .. }
             | ExprKind::Let { .. }
             | ExprKind::Assign { .. }
             | ExprKind::AssignOp { .. }
             | ExprKind::Break { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. }
             | ExprKind::Literal { .. }
diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
index b23bc089cd4..9e07dd5da7e 100644
--- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs
@@ -538,6 +538,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | ExprKind::RawBorrow { .. }
             | ExprKind::Adt { .. }
             | ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::LogicalOp { .. }
             | ExprKind::Call { .. }
             | ExprKind::Field { .. }
@@ -548,6 +549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             | ExprKind::UpvarRef { .. }
             | ExprKind::Break { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. }
             | ExprKind::InlineAsm { .. }
diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs
index 34524aed406..5e4219dbf5b 100644
--- a/compiler/rustc_mir_build/src/builder/expr/category.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/category.rs
@@ -83,9 +83,11 @@ impl Category {
             | ExprKind::NamedConst { .. } => Some(Category::Constant),
 
             ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::Block { .. }
             | ExprKind::Break { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. } =>
             // FIXME(#27840) these probably want their own
diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs
index 2074fbce0ae..fe3d072fa88 100644
--- a/compiler/rustc_mir_build/src/builder/expr/into.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/into.rs
@@ -8,15 +8,16 @@ use rustc_hir::lang_items::LangItem;
 use rustc_middle::mir::*;
 use rustc_middle::span_bug;
 use rustc_middle::thir::*;
-use rustc_middle::ty::{CanonicalUserTypeAnnotation, Ty};
+use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
 use rustc_span::DUMMY_SP;
 use rustc_span::source_map::Spanned;
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::{debug, instrument};
 
 use crate::builder::expr::category::{Category, RvalueFunc};
-use crate::builder::matches::DeclareLetBindings;
+use crate::builder::matches::{DeclareLetBindings, HasMatchGuard};
 use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
+use crate::errors::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// Compile `expr`, storing the result into `destination`, which
@@ -244,6 +245,122 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     None
                 })
             }
+            ExprKind::LoopMatch { state, region_scope, match_span, ref arms } => {
+                // Intuitively, this is a combination of a loop containing a labeled block
+                // containing a match.
+                //
+                // The only new bit here is that the lowering of the match is wrapped in a
+                // `in_const_continuable_scope`, which makes the match arms and their target basic
+                // block available to the lowering of `#[const_continue]`.
+
+                fn is_supported_loop_match_type(ty: Ty<'_>) -> bool {
+                    match ty.kind() {
+                        ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => true,
+                        ty::Adt(adt_def, _) => match adt_def.adt_kind() {
+                            ty::AdtKind::Struct | ty::AdtKind::Union => false,
+                            ty::AdtKind::Enum => {
+                                adt_def.variants().iter().all(|v| v.fields.is_empty())
+                            }
+                        },
+                        _ => false,
+                    }
+                }
+
+                let state_ty = this.thir.exprs[state].ty;
+                if !is_supported_loop_match_type(state_ty) {
+                    let span = this.thir.exprs[state].span;
+                    this.tcx.dcx().emit_fatal(LoopMatchUnsupportedType { span, ty: state_ty })
+                }
+
+                let loop_block = this.cfg.start_new_block();
+
+                // Start the loop.
+                this.cfg.goto(block, source_info, loop_block);
+
+                this.in_breakable_scope(Some(loop_block), destination, expr_span, |this| {
+                    // Logic for `loop`.
+                    let mut body_block = this.cfg.start_new_block();
+                    this.cfg.terminate(
+                        loop_block,
+                        source_info,
+                        TerminatorKind::FalseUnwind {
+                            real_target: body_block,
+                            unwind: UnwindAction::Continue,
+                        },
+                    );
+                    this.diverge_from(loop_block);
+
+                    // Logic for `match`.
+                    let scrutinee_place_builder =
+                        unpack!(body_block = this.as_place_builder(body_block, state));
+                    let scrutinee_span = this.thir.exprs[state].span;
+                    let match_start_span = match_span.shrink_to_lo().to(scrutinee_span);
+
+                    let mut patterns = Vec::with_capacity(arms.len());
+                    for &arm_id in arms.iter() {
+                        let arm = &this.thir[arm_id];
+
+                        if let Some(guard) = arm.guard {
+                            let span = this.thir.exprs[guard].span;
+                            this.tcx.dcx().emit_fatal(LoopMatchArmWithGuard { span })
+                        }
+
+                        patterns.push((&*arm.pattern, HasMatchGuard::No));
+                    }
+
+                    // The `built_tree` maps match arms to their basic block (where control flow
+                    // jumps to when a value matches the arm). This structure is stored so that a
+                    // `#[const_continue]` can figure out what basic block to jump to.
+                    let built_tree = this.lower_match_tree(
+                        body_block,
+                        scrutinee_span,
+                        &scrutinee_place_builder,
+                        match_start_span,
+                        patterns,
+                        false,
+                    );
+
+                    let state_place = scrutinee_place_builder.to_place(this);
+
+                    // This is logic for the labeled block: a block is a drop scope, hence
+                    // `in_scope`, and a labeled block can be broken out of with a `break 'label`,
+                    // hence the `in_breakable_scope`.
+                    //
+                    // Then `in_const_continuable_scope` stores information for the lowering of
+                    // `#[const_continue]`, and finally the match is lowered in the standard way.
+                    unpack!(
+                        body_block = this.in_scope(
+                            (region_scope, source_info),
+                            LintLevel::Inherited,
+                            move |this| {
+                                this.in_breakable_scope(None, state_place, expr_span, |this| {
+                                    Some(this.in_const_continuable_scope(
+                                        arms.clone(),
+                                        built_tree.clone(),
+                                        state_place,
+                                        expr_span,
+                                        |this| {
+                                            this.lower_match_arms(
+                                                destination,
+                                                scrutinee_place_builder,
+                                                scrutinee_span,
+                                                arms,
+                                                built_tree,
+                                                this.source_info(match_span),
+                                            )
+                                        },
+                                    ))
+                                })
+                            }
+                        )
+                    );
+
+                    this.cfg.goto(body_block, source_info, loop_block);
+
+                    // Loops are only exited by `break` expressions.
+                    None
+                })
+            }
             ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
                 let fun = unpack!(block = this.as_local_operand(block, fun));
                 let args: Box<[_]> = args
@@ -601,6 +718,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
 
             ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Break { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. } => {
diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
index 2dff26f02f3..675beceea14 100644
--- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs
@@ -98,6 +98,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             ExprKind::Break { label, value } => {
                 this.break_scope(block, value, BreakableTarget::Break(label), source_info)
             }
+            ExprKind::ConstContinue { label, value } => {
+                this.break_const_continuable_scope(block, value, label, source_info)
+            }
             ExprKind::Return { value } => {
                 this.break_scope(block, value, BreakableTarget::Return, source_info)
             }
diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs
index 977d4f3e931..270a7d4b154 100644
--- a/compiler/rustc_mir_build/src/builder/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs
@@ -18,7 +18,9 @@ use rustc_middle::bug;
 use rustc_middle::middle::region;
 use rustc_middle::mir::{self, *};
 use rustc_middle::thir::{self, *};
-use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
+use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
+use rustc_pattern_analysis::constructor::RangeEnd;
+use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
 use rustc_span::{BytePos, Pos, Span, Symbol, sym};
 use tracing::{debug, instrument};
 
@@ -426,7 +428,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// (by [Builder::lower_match_tree]).
     ///
     /// `outer_source_info` is the SourceInfo for the whole match.
-    fn lower_match_arms(
+    pub(crate) fn lower_match_arms(
         &mut self,
         destination: Place<'tcx>,
         scrutinee_place_builder: PlaceBuilder<'tcx>,
@@ -1395,7 +1397,7 @@ pub(crate) struct ArmHasGuard(pub(crate) bool);
 /// A sub-branch in the output of match lowering. Match lowering has generated MIR code that will
 /// branch to `success_block` when the matched value matches the corresponding pattern. If there is
 /// a guard, its failure must continue to `otherwise_block`, which will resume testing patterns.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 struct MatchTreeSubBranch<'tcx> {
     span: Span,
     /// The block that is branched to if the corresponding subpattern matches.
@@ -1411,7 +1413,7 @@ struct MatchTreeSubBranch<'tcx> {
 }
 
 /// A branch in the output of match lowering.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 struct MatchTreeBranch<'tcx> {
     sub_branches: Vec<MatchTreeSubBranch<'tcx>>,
 }
@@ -1430,8 +1432,8 @@ struct MatchTreeBranch<'tcx> {
 /// Here the first arm gives the first `MatchTreeBranch`, which has two sub-branches, one for each
 /// alternative of the or-pattern. They are kept separate because each needs to bind `x` to a
 /// different place.
-#[derive(Debug)]
-struct BuiltMatchTree<'tcx> {
+#[derive(Debug, Clone)]
+pub(crate) struct BuiltMatchTree<'tcx> {
     branches: Vec<MatchTreeBranch<'tcx>>,
     otherwise_block: BasicBlock,
     /// If any of the branches had a guard, we collect here the places and locals to fakely borrow
@@ -1489,7 +1491,7 @@ impl<'tcx> MatchTreeBranch<'tcx> {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum HasMatchGuard {
+pub(crate) enum HasMatchGuard {
     Yes,
     No,
 }
@@ -1504,7 +1506,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
     /// or not (for `let` and `match`). In the refutable case we return the block to which we branch
     /// on failure.
-    fn lower_match_tree(
+    pub(crate) fn lower_match_tree(
         &mut self,
         block: BasicBlock,
         scrutinee_span: Span,
@@ -1890,7 +1892,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats);
         candidate.or_span = Some(match_pair.pattern_span);
         candidate.subcandidates = pats
-            .into_vec()
             .into_iter()
             .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
             .collect();
@@ -2864,4 +2865,129 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         true
     }
+
+    /// Attempt to statically pick the `BasicBlock` that a value would resolve to at runtime.
+    pub(crate) fn static_pattern_match(
+        &self,
+        cx: &RustcPatCtxt<'_, 'tcx>,
+        valtree: ValTree<'tcx>,
+        arms: &[ArmId],
+        built_match_tree: &BuiltMatchTree<'tcx>,
+    ) -> Option<BasicBlock> {
+        let it = arms.iter().zip(built_match_tree.branches.iter());
+        for (&arm_id, branch) in it {
+            let pat = cx.lower_pat(&*self.thir.arms[arm_id].pattern);
+
+            // Peel off or-patterns if they exist.
+            if let rustc_pattern_analysis::rustc::Constructor::Or = pat.ctor() {
+                for pat in pat.iter_fields() {
+                    // For top-level or-patterns (the only ones we accept right now), when the
+                    // bindings are the same (e.g. there are none), the sub_branch is stored just
+                    // once.
+                    let sub_branch = branch
+                        .sub_branches
+                        .get(pat.idx)
+                        .or_else(|| branch.sub_branches.last())
+                        .unwrap();
+
+                    match self.static_pattern_match_inner(valtree, &pat.pat) {
+                        true => return Some(sub_branch.success_block),
+                        false => continue,
+                    }
+                }
+            } else if self.static_pattern_match_inner(valtree, &pat) {
+                return Some(branch.sub_branches[0].success_block);
+            }
+        }
+
+        None
+    }
+
+    /// Helper for [`Self::static_pattern_match`], checking whether the value represented by the
+    /// `ValTree` matches the given pattern. This function does not recurse, meaning that it does
+    /// not handle or-patterns, or patterns for types with fields.
+    fn static_pattern_match_inner(
+        &self,
+        valtree: ty::ValTree<'tcx>,
+        pat: &DeconstructedPat<'_, 'tcx>,
+    ) -> bool {
+        use rustc_pattern_analysis::constructor::{IntRange, MaybeInfiniteInt};
+        use rustc_pattern_analysis::rustc::Constructor;
+
+        match pat.ctor() {
+            Constructor::Variant(variant_index) => {
+                let ValTreeKind::Branch(box [actual_variant_idx]) = *valtree else {
+                    bug!("malformed valtree for an enum")
+                };
+
+                let ValTreeKind::Leaf(actual_variant_idx) = ***actual_variant_idx else {
+                    bug!("malformed valtree for an enum")
+                };
+
+                *variant_index == VariantIdx::from_u32(actual_variant_idx.to_u32())
+            }
+            Constructor::IntRange(int_range) => {
+                let size = pat.ty().primitive_size(self.tcx);
+                let actual_int = valtree.unwrap_leaf().to_bits(size);
+                let actual_int = if pat.ty().is_signed() {
+                    MaybeInfiniteInt::new_finite_int(actual_int, size.bits())
+                } else {
+                    MaybeInfiniteInt::new_finite_uint(actual_int)
+                };
+                IntRange::from_singleton(actual_int).is_subrange(int_range)
+            }
+            Constructor::Bool(pattern_value) => match valtree.unwrap_leaf().try_to_bool() {
+                Ok(actual_value) => *pattern_value == actual_value,
+                Err(()) => bug!("bool value with invalid bits"),
+            },
+            Constructor::F16Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f16();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::F32Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f32();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::F64Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f64();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::F128Range(l, h, end) => {
+                let actual = valtree.unwrap_leaf().to_f128();
+                match end {
+                    RangeEnd::Included => (*l..=*h).contains(&actual),
+                    RangeEnd::Excluded => (*l..*h).contains(&actual),
+                }
+            }
+            Constructor::Wildcard => true,
+
+            // These we may eventually support:
+            Constructor::Struct
+            | Constructor::Ref
+            | Constructor::DerefPattern(_)
+            | Constructor::Slice(_)
+            | Constructor::UnionField
+            | Constructor::Or
+            | Constructor::Str(_) => bug!("unsupported pattern constructor {:?}", pat.ctor()),
+
+            // These should never occur here:
+            Constructor::Opaque(_)
+            | Constructor::Never
+            | Constructor::NonExhaustive
+            | Constructor::Hidden
+            | Constructor::Missing
+            | Constructor::PrivateUninhabited => {
+                bug!("unsupported pattern constructor {:?}", pat.ctor())
+            }
+        }
+    }
 }
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index 67988f1fcbc..1d15e7e126f 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -83,20 +83,24 @@ that contains only loops and breakable blocks. It tracks where a `break`,
 
 use std::mem;
 
+use interpret::ErrorHandled;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::HirId;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::middle::region;
-use rustc_middle::mir::*;
-use rustc_middle::thir::{ExprId, LintLevel};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::mir::{self, *};
+use rustc_middle::thir::{AdtExpr, AdtExprBase, ArmId, ExprId, ExprKind, LintLevel};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
 use rustc_middle::{bug, span_bug};
+use rustc_pattern_analysis::rustc::RustcPatCtxt;
 use rustc_session::lint::Level;
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
+use super::matches::BuiltMatchTree;
 use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
+use crate::errors::{ConstContinueBadConst, ConstContinueUnknownJumpTarget};
 
 #[derive(Debug)]
 pub(crate) struct Scopes<'tcx> {
@@ -105,6 +109,8 @@ pub(crate) struct Scopes<'tcx> {
     /// The current set of breakable scopes. See module comment for more details.
     breakable_scopes: Vec<BreakableScope<'tcx>>,
 
+    const_continuable_scopes: Vec<ConstContinuableScope<'tcx>>,
+
     /// The scope of the innermost if-then currently being lowered.
     if_then_scope: Option<IfThenScope>,
 
@@ -175,6 +181,20 @@ struct BreakableScope<'tcx> {
 }
 
 #[derive(Debug)]
+struct ConstContinuableScope<'tcx> {
+    /// The scope for the `#[loop_match]` which its `#[const_continue]`s will jump to.
+    region_scope: region::Scope,
+    /// The place of the state of a `#[loop_match]`, which a `#[const_continue]` must update.
+    state_place: Place<'tcx>,
+
+    arms: Box<[ArmId]>,
+    built_match_tree: BuiltMatchTree<'tcx>,
+
+    /// Drops that happen on a `#[const_continue]`
+    const_continue_drops: DropTree,
+}
+
+#[derive(Debug)]
 struct IfThenScope {
     /// The if-then scope or arm scope
     region_scope: region::Scope,
@@ -461,6 +481,7 @@ impl<'tcx> Scopes<'tcx> {
         Self {
             scopes: Vec::new(),
             breakable_scopes: Vec::new(),
+            const_continuable_scopes: Vec::new(),
             if_then_scope: None,
             unwind_drops: DropTree::new(),
             coroutine_drops: DropTree::new(),
@@ -552,6 +573,59 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    /// Start a const-continuable scope, which tracks where `#[const_continue] break` should
+    /// branch to.
+    pub(crate) fn in_const_continuable_scope<F>(
+        &mut self,
+        arms: Box<[ArmId]>,
+        built_match_tree: BuiltMatchTree<'tcx>,
+        state_place: Place<'tcx>,
+        span: Span,
+        f: F,
+    ) -> BlockAnd<()>
+    where
+        F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<()>,
+    {
+        let region_scope = self.scopes.topmost();
+        let scope = ConstContinuableScope {
+            region_scope,
+            state_place,
+            const_continue_drops: DropTree::new(),
+            arms,
+            built_match_tree,
+        };
+        self.scopes.const_continuable_scopes.push(scope);
+        let normal_exit_block = f(self);
+        let const_continue_scope = self.scopes.const_continuable_scopes.pop().unwrap();
+        assert!(const_continue_scope.region_scope == region_scope);
+
+        let break_block = self.build_exit_tree(
+            const_continue_scope.const_continue_drops,
+            region_scope,
+            span,
+            None,
+        );
+
+        match (normal_exit_block, break_block) {
+            (block, None) => block,
+            (normal_block, Some(exit_block)) => {
+                let target = self.cfg.start_new_block();
+                let source_info = self.source_info(span);
+                self.cfg.terminate(
+                    normal_block.into_block(),
+                    source_info,
+                    TerminatorKind::Goto { target },
+                );
+                self.cfg.terminate(
+                    exit_block.into_block(),
+                    source_info,
+                    TerminatorKind::Goto { target },
+                );
+                target.unit()
+            }
+        }
+    }
+
     /// Start an if-then scope which tracks drop for `if` expressions and `if`
     /// guards.
     ///
@@ -742,6 +816,190 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         self.cfg.start_new_block().unit()
     }
 
+    /// Based on `FunctionCx::eval_unevaluated_mir_constant_to_valtree`.
+    fn eval_unevaluated_mir_constant_to_valtree(
+        &self,
+        constant: ConstOperand<'tcx>,
+    ) -> Result<(ty::ValTree<'tcx>, Ty<'tcx>), interpret::ErrorHandled> {
+        assert!(!constant.const_.ty().has_param());
+        let (uv, ty) = match constant.const_ {
+            mir::Const::Unevaluated(uv, ty) => (uv.shrink(), ty),
+            mir::Const::Ty(_, c) => match c.kind() {
+                // A constant that came from a const generic but was then used as an argument to
+                // old-style simd_shuffle (passing as argument instead of as a generic param).
+                ty::ConstKind::Value(cv) => return Ok((cv.valtree, cv.ty)),
+                other => span_bug!(constant.span, "{other:#?}"),
+            },
+            mir::Const::Val(mir::ConstValue::Scalar(mir::interpret::Scalar::Int(val)), ty) => {
+                return Ok((ValTree::from_scalar_int(self.tcx, val), ty));
+            }
+            // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
+            // a constant and write that value back into `Operand`s. This could happen, but is
+            // unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take
+            // a lot of care around intrinsics. For an issue to happen here, it would require a
+            // macro expanding to a `simd_shuffle` call without wrapping the constant argument in a
+            // `const {}` block, but the user pass through arbitrary expressions.
+
+            // FIXME(oli-obk): Replace the magic const generic argument of `simd_shuffle` with a
+            // real const generic, and get rid of this entire function.
+            other => span_bug!(constant.span, "{other:#?}"),
+        };
+
+        match self.tcx.const_eval_resolve_for_typeck(self.typing_env(), uv, constant.span) {
+            Ok(Ok(valtree)) => Ok((valtree, ty)),
+            Ok(Err(ty)) => span_bug!(constant.span, "could not convert {ty:?} to a valtree"),
+            Err(e) => Err(e),
+        }
+    }
+
+    /// Sets up the drops for jumping from `block` to `scope`.
+    pub(crate) fn break_const_continuable_scope(
+        &mut self,
+        mut block: BasicBlock,
+        value: ExprId,
+        scope: region::Scope,
+        source_info: SourceInfo,
+    ) -> BlockAnd<()> {
+        let span = source_info.span;
+
+        // A break can only break out of a scope, so the value should be a scope.
+        let rustc_middle::thir::ExprKind::Scope { value, .. } = self.thir[value].kind else {
+            span_bug!(span, "break value must be a scope")
+        };
+
+        let constant = match &self.thir[value].kind {
+            ExprKind::Adt(box AdtExpr { variant_index, fields, base, .. }) => {
+                assert!(matches!(base, AdtExprBase::None));
+                assert!(fields.is_empty());
+                ConstOperand {
+                    span: self.thir[value].span,
+                    user_ty: None,
+                    const_: Const::Ty(
+                        self.thir[value].ty,
+                        ty::Const::new_value(
+                            self.tcx,
+                            ValTree::from_branches(
+                                self.tcx,
+                                [ValTree::from_scalar_int(self.tcx, variant_index.as_u32().into())],
+                            ),
+                            self.thir[value].ty,
+                        ),
+                    ),
+                }
+            }
+            _ => self.as_constant(&self.thir[value]),
+        };
+
+        let break_index = self
+            .scopes
+            .const_continuable_scopes
+            .iter()
+            .rposition(|const_continuable_scope| const_continuable_scope.region_scope == scope)
+            .unwrap_or_else(|| span_bug!(span, "no enclosing const-continuable scope found"));
+
+        let scope = &self.scopes.const_continuable_scopes[break_index];
+
+        let state_decl = &self.local_decls[scope.state_place.as_local().unwrap()];
+        let state_ty = state_decl.ty;
+        let (discriminant_ty, rvalue) = match state_ty.kind() {
+            ty::Adt(adt_def, _) if adt_def.is_enum() => {
+                (state_ty.discriminant_ty(self.tcx), Rvalue::Discriminant(scope.state_place))
+            }
+            ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Bool | ty::Char => {
+                (state_ty, Rvalue::Use(Operand::Copy(scope.state_place)))
+            }
+            _ => span_bug!(state_decl.source_info.span, "unsupported #[loop_match] state"),
+        };
+
+        // The `PatCtxt` is normally used in pattern exhaustiveness checking, but reused
+        // here because it performs normalization and const evaluation.
+        let dropless_arena = rustc_arena::DroplessArena::default();
+        let typeck_results = self.tcx.typeck(self.def_id);
+        let cx = RustcPatCtxt {
+            tcx: self.tcx,
+            typeck_results,
+            module: self.tcx.parent_module(self.hir_id).to_def_id(),
+            // FIXME(#132279): We're in a body, should handle opaques.
+            typing_env: rustc_middle::ty::TypingEnv::non_body_analysis(self.tcx, self.def_id),
+            dropless_arena: &dropless_arena,
+            match_lint_level: self.hir_id,
+            whole_match_span: Some(rustc_span::Span::default()),
+            scrut_span: rustc_span::Span::default(),
+            refutable: true,
+            known_valid_scrutinee: true,
+        };
+
+        let valtree = match self.eval_unevaluated_mir_constant_to_valtree(constant) {
+            Ok((valtree, ty)) => {
+                // Defensively check that the type is monomorphic.
+                assert!(!ty.has_param());
+
+                valtree
+            }
+            Err(ErrorHandled::Reported(..)) => return self.cfg.start_new_block().unit(),
+            Err(ErrorHandled::TooGeneric(_)) => {
+                self.tcx.dcx().emit_fatal(ConstContinueBadConst { span: constant.span });
+            }
+        };
+
+        let Some(real_target) =
+            self.static_pattern_match(&cx, valtree, &*scope.arms, &scope.built_match_tree)
+        else {
+            self.tcx.dcx().emit_fatal(ConstContinueUnknownJumpTarget { span })
+        };
+
+        self.block_context.push(BlockFrame::SubExpr);
+        let state_place = scope.state_place;
+        block = self.expr_into_dest(state_place, block, value).into_block();
+        self.block_context.pop();
+
+        let discr = self.temp(discriminant_ty, source_info.span);
+        let scope_index = self
+            .scopes
+            .scope_index(self.scopes.const_continuable_scopes[break_index].region_scope, span);
+        let scope = &mut self.scopes.const_continuable_scopes[break_index];
+        self.cfg.push_assign(block, source_info, discr, rvalue);
+        let drop_and_continue_block = self.cfg.start_new_block();
+        let imaginary_target = self.cfg.start_new_block();
+        self.cfg.terminate(
+            block,
+            source_info,
+            TerminatorKind::FalseEdge { real_target: drop_and_continue_block, imaginary_target },
+        );
+
+        let drops = &mut scope.const_continue_drops;
+
+        let drop_idx = self.scopes.scopes[scope_index + 1..]
+            .iter()
+            .flat_map(|scope| &scope.drops)
+            .fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
+
+        drops.add_entry_point(imaginary_target, drop_idx);
+
+        self.cfg.terminate(imaginary_target, source_info, TerminatorKind::UnwindResume);
+
+        let region_scope = scope.region_scope;
+        let scope_index = self.scopes.scope_index(region_scope, span);
+        let mut drops = DropTree::new();
+
+        let drop_idx = self.scopes.scopes[scope_index + 1..]
+            .iter()
+            .flat_map(|scope| &scope.drops)
+            .fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx));
+
+        drops.add_entry_point(drop_and_continue_block, drop_idx);
+
+        // `build_drop_trees` doesn't have access to our source_info, so we
+        // create a dummy terminator now. `TerminatorKind::UnwindResume` is used
+        // because MIR type checking will panic if it hasn't been overwritten.
+        // (See `<ExitScopes as DropTreeBuilder>::link_entry_point`.)
+        self.cfg.terminate(drop_and_continue_block, source_info, TerminatorKind::UnwindResume);
+
+        self.build_exit_tree(drops, region_scope, span, Some(real_target));
+
+        return self.cfg.start_new_block().unit();
+    }
+
     /// Sets up the drops for breaking from `block` due to an `if` condition
     /// that turned out to be false.
     ///
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index d5061b71699..0b6b36640e9 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -465,10 +465,12 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             | ExprKind::Break { .. }
             | ExprKind::Closure { .. }
             | ExprKind::Continue { .. }
+            | ExprKind::ConstContinue { .. }
             | ExprKind::Return { .. }
             | ExprKind::Become { .. }
             | ExprKind::Yield { .. }
             | ExprKind::Loop { .. }
+            | ExprKind::LoopMatch { .. }
             | ExprKind::Let { .. }
             | ExprKind::Match { .. }
             | ExprKind::Box { .. }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index ae09db50235..32df191cbca 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1149,3 +1149,80 @@ impl Subdiagnostic for Rust2024IncompatiblePatSugg {
         }
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_invalid_update)]
+pub(crate) struct LoopMatchInvalidUpdate {
+    #[primary_span]
+    pub lhs: Span,
+    #[label]
+    pub scrutinee: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_invalid_match)]
+#[note]
+pub(crate) struct LoopMatchInvalidMatch {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_unsupported_type)]
+#[note]
+pub(crate) struct LoopMatchUnsupportedType<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_bad_statements)]
+pub(crate) struct LoopMatchBadStatements {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_bad_rhs)]
+pub(crate) struct LoopMatchBadRhs {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_missing_assignment)]
+pub(crate) struct LoopMatchMissingAssignment {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_loop_match_arm_with_guard)]
+pub(crate) struct LoopMatchArmWithGuard {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_continue_bad_const)]
+pub(crate) struct ConstContinueBadConst {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_continue_missing_value)]
+pub(crate) struct ConstContinueMissingValue {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_continue_unknown_jump_target)]
+#[note]
+pub(crate) struct ConstContinueUnknownJumpTarget {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 3baeccf6409..5197e93fda7 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -1,6 +1,7 @@
 use itertools::Itertools;
 use rustc_abi::{FIRST_VARIANT, FieldIdx};
 use rustc_ast::UnsafeBinderCastKind;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@@ -21,6 +22,7 @@ use rustc_middle::{bug, span_bug};
 use rustc_span::{Span, sym};
 use tracing::{debug, info, instrument, trace};
 
+use crate::errors::*;
 use crate::thir::cx::ThirBuildCx;
 
 impl<'tcx> ThirBuildCx<'tcx> {
@@ -479,6 +481,55 @@ impl<'tcx> ThirBuildCx<'tcx> {
                 ExprKind::RawBorrow { mutability, arg: self.mirror_expr(arg) }
             }
 
+            // Make `&pin mut $expr` and `&pin const $expr` into
+            // `Pin { __pointer: &mut { $expr } }` and `Pin { __pointer: &$expr }`.
+            hir::ExprKind::AddrOf(hir::BorrowKind::Pin, mutbl, arg_expr) => match expr_ty.kind() {
+                &ty::Adt(adt_def, args) if tcx.is_lang_item(adt_def.did(), hir::LangItem::Pin) => {
+                    let ty = args.type_at(0);
+                    let arg_ty = self.typeck_results.expr_ty(arg_expr);
+                    let mut arg = self.mirror_expr(arg_expr);
+                    // For `&pin mut $place` where `$place` is not `Unpin`, move the place
+                    // `$place` to ensure it will not be used afterwards.
+                    if mutbl.is_mut() && !arg_ty.is_unpin(self.tcx, self.typing_env) {
+                        let block = self.thir.blocks.push(Block {
+                            targeted_by_break: false,
+                            region_scope: region::Scope {
+                                local_id: arg_expr.hir_id.local_id,
+                                data: region::ScopeData::Node,
+                            },
+                            span: arg_expr.span,
+                            stmts: Box::new([]),
+                            expr: Some(arg),
+                            safety_mode: BlockSafety::Safe,
+                        });
+                        let (temp_lifetime, backwards_incompatible) = self
+                            .rvalue_scopes
+                            .temporary_scope(self.region_scope_tree, arg_expr.hir_id.local_id);
+                        arg = self.thir.exprs.push(Expr {
+                            temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
+                            ty: arg_ty,
+                            span: arg_expr.span,
+                            kind: ExprKind::Block { block },
+                        });
+                    }
+                    let expr = self.thir.exprs.push(Expr {
+                        temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
+                        ty,
+                        span: expr.span,
+                        kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg },
+                    });
+                    ExprKind::Adt(Box::new(AdtExpr {
+                        adt_def,
+                        variant_index: FIRST_VARIANT,
+                        args,
+                        fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
+                        user_ty: None,
+                        base: AdtExprBase::None,
+                    }))
+                }
+                _ => span_bug!(expr.span, "unexpected type for pinned borrow: {:?}", expr_ty),
+            },
+
             hir::ExprKind::Block(blk, _) => ExprKind::Block { block: self.mirror_block(blk) },
 
             hir::ExprKind::Assign(lhs, rhs, _) => {
@@ -796,16 +847,38 @@ impl<'tcx> ThirBuildCx<'tcx> {
             }
             hir::ExprKind::Ret(v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
             hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
-            hir::ExprKind::Break(dest, ref value) => match dest.target_id {
-                Ok(target_id) => ExprKind::Break {
-                    label: region::Scope {
-                        local_id: target_id.local_id,
-                        data: region::ScopeData::Node,
-                    },
-                    value: value.map(|value| self.mirror_expr(value)),
-                },
-                Err(err) => bug!("invalid loop id for break: {}", err),
-            },
+            hir::ExprKind::Break(dest, ref value) => {
+                if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::ConstContinue(_)) {
+                    match dest.target_id {
+                        Ok(target_id) => {
+                            let Some(value) = value else {
+                                let span = expr.span;
+                                self.tcx.dcx().emit_fatal(ConstContinueMissingValue { span })
+                            };
+
+                            ExprKind::ConstContinue {
+                                label: region::Scope {
+                                    local_id: target_id.local_id,
+                                    data: region::ScopeData::Node,
+                                },
+                                value: self.mirror_expr(value),
+                            }
+                        }
+                        Err(err) => bug!("invalid loop id for break: {}", err),
+                    }
+                } else {
+                    match dest.target_id {
+                        Ok(target_id) => ExprKind::Break {
+                            label: region::Scope {
+                                local_id: target_id.local_id,
+                                data: region::ScopeData::Node,
+                            },
+                            value: value.map(|value| self.mirror_expr(value)),
+                        },
+                        Err(err) => bug!("invalid loop id for break: {}", err),
+                    }
+                }
+            }
             hir::ExprKind::Continue(dest) => match dest.target_id {
                 Ok(loop_id) => ExprKind::Continue {
                     label: region::Scope {
@@ -840,18 +913,93 @@ impl<'tcx> ThirBuildCx<'tcx> {
                 match_source,
             },
             hir::ExprKind::Loop(body, ..) => {
-                let block_ty = self.typeck_results.node_type(body.hir_id);
-                let (temp_lifetime, backwards_incompatible) = self
-                    .rvalue_scopes
-                    .temporary_scope(self.region_scope_tree, body.hir_id.local_id);
-                let block = self.mirror_block(body);
-                let body = self.thir.exprs.push(Expr {
-                    ty: block_ty,
-                    temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
-                    span: self.thir[block].span,
-                    kind: ExprKind::Block { block },
-                });
-                ExprKind::Loop { body }
+                if find_attr!(self.tcx.hir_attrs(expr.hir_id), AttributeKind::LoopMatch(_)) {
+                    let dcx = self.tcx.dcx();
+
+                    // Accept either `state = expr` or `state = expr;`.
+                    let loop_body_expr = match body.stmts {
+                        [] => match body.expr {
+                            Some(expr) => expr,
+                            None => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
+                        },
+                        [single] if body.expr.is_none() => match single.kind {
+                            hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => expr,
+                            _ => dcx.emit_fatal(LoopMatchMissingAssignment { span: body.span }),
+                        },
+                        [first @ last] | [first, .., last] => dcx
+                            .emit_fatal(LoopMatchBadStatements { span: first.span.to(last.span) }),
+                    };
+
+                    let hir::ExprKind::Assign(state, rhs_expr, _) = loop_body_expr.kind else {
+                        dcx.emit_fatal(LoopMatchMissingAssignment { span: loop_body_expr.span })
+                    };
+
+                    let hir::ExprKind::Block(block_body, _) = rhs_expr.kind else {
+                        dcx.emit_fatal(LoopMatchBadRhs { span: rhs_expr.span })
+                    };
+
+                    // The labeled block should contain one match expression, but defining items is
+                    // allowed.
+                    for stmt in block_body.stmts {
+                        if !matches!(stmt.kind, rustc_hir::StmtKind::Item(_)) {
+                            dcx.emit_fatal(LoopMatchBadStatements { span: stmt.span })
+                        }
+                    }
+
+                    let Some(block_body_expr) = block_body.expr else {
+                        dcx.emit_fatal(LoopMatchBadRhs { span: block_body.span })
+                    };
+
+                    let hir::ExprKind::Match(scrutinee, arms, _match_source) = block_body_expr.kind
+                    else {
+                        dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span })
+                    };
+
+                    fn local(expr: &rustc_hir::Expr<'_>) -> Option<hir::HirId> {
+                        if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind {
+                            if let Res::Local(hir_id) = path.res {
+                                return Some(hir_id);
+                            }
+                        }
+
+                        None
+                    }
+
+                    let Some(scrutinee_hir_id) = local(scrutinee) else {
+                        dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span })
+                    };
+
+                    if local(state) != Some(scrutinee_hir_id) {
+                        dcx.emit_fatal(LoopMatchInvalidUpdate {
+                            scrutinee: scrutinee.span,
+                            lhs: state.span,
+                        })
+                    }
+
+                    ExprKind::LoopMatch {
+                        state: self.mirror_expr(state),
+                        region_scope: region::Scope {
+                            local_id: block_body.hir_id.local_id,
+                            data: region::ScopeData::Node,
+                        },
+
+                        arms: arms.iter().map(|a| self.convert_arm(a)).collect(),
+                        match_span: block_body_expr.span,
+                    }
+                } else {
+                    let block_ty = self.typeck_results.node_type(body.hir_id);
+                    let (temp_lifetime, backwards_incompatible) = self
+                        .rvalue_scopes
+                        .temporary_scope(self.region_scope_tree, body.hir_id.local_id);
+                    let block = self.mirror_block(body);
+                    let body = self.thir.exprs.push(Expr {
+                        ty: block_ty,
+                        temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
+                        span: self.thir[block].span,
+                        kind: ExprKind::Block { block },
+                    });
+                    ExprKind::Loop { body }
+                }
             }
             hir::ExprKind::Field(source, ..) => ExprKind::Field {
                 lhs: self.mirror_expr(source),
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 245bd866030..9fd410e6bf1 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -331,7 +331,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
 
             // These diverge.
-            Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
+            Become { .. }
+            | Break { .. }
+            | Continue { .. }
+            | ConstContinue { .. }
+            | Return { .. } => true,
 
             // These are statements that evaluate to `()`.
             Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
@@ -353,6 +357,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
             | Literal { .. }
             | LogicalOp { .. }
             | Loop { .. }
+            | LoopMatch { .. }
             | Match { .. }
             | NamedConst { .. }
             | NonHirLiteral { .. }
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index db9547a481f..1507b6b8c06 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -318,6 +318,20 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
                 self.print_expr(*body, depth_lvl + 2);
                 print_indented!(self, ")", depth_lvl);
             }
+            LoopMatch { state, region_scope, match_span, arms } => {
+                print_indented!(self, "LoopMatch {", depth_lvl);
+                print_indented!(self, "state:", depth_lvl + 1);
+                self.print_expr(*state, depth_lvl + 2);
+                print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1);
+                print_indented!(self, format!("match_span: {:?}", match_span), depth_lvl + 1);
+
+                print_indented!(self, "arms: [", depth_lvl + 1);
+                for arm_id in arms.iter() {
+                    self.print_arm(*arm_id, depth_lvl + 2);
+                }
+                print_indented!(self, "]", depth_lvl + 1);
+                print_indented!(self, "}", depth_lvl);
+            }
             Let { expr, pat } => {
                 print_indented!(self, "Let {", depth_lvl);
                 print_indented!(self, "expr:", depth_lvl + 1);
@@ -415,6 +429,13 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
                 print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
                 print_indented!(self, "}", depth_lvl);
             }
+            ConstContinue { label, value } => {
+                print_indented!(self, "ConstContinue (", depth_lvl);
+                print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
+                print_indented!(self, "value:", depth_lvl + 1);
+                self.print_expr(*value, depth_lvl + 2);
+                print_indented!(self, ")", depth_lvl);
+            }
             Return { value } => {
                 print_indented!(self, "Return {", depth_lvl);
                 print_indented!(self, "value:", depth_lvl + 1);
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 93c76c47f06..4e312aab497 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -847,7 +847,7 @@ impl<'a> Parser<'a> {
         self.dcx().emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span });
     }
 
-    /// Parse `mut?` or `raw [ const | mut ]`.
+    /// Parse `mut?` or `[ raw | pin ] [ const | mut ]`.
     fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) {
         if self.check_keyword(exp!(Raw)) && self.look_ahead(1, Token::is_mutability) {
             // `raw [ const | mut ]`.
@@ -855,6 +855,11 @@ impl<'a> Parser<'a> {
             assert!(found_raw);
             let mutability = self.parse_const_or_mut().unwrap();
             (ast::BorrowKind::Raw, mutability)
+        } else if let Some((ast::Pinnedness::Pinned, mutbl)) = self.parse_pin_and_mut() {
+            // `pin [ const | mut ]`.
+            // `pin` has been gated in `self.parse_pin_and_mut()` so we don't
+            // need to gate it here.
+            (ast::BorrowKind::Pin, mutbl)
         } else {
             // `mut?`
             (ast::BorrowKind::Ref, self.parse_mutability())
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 47e29923ee5..68d78af5943 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -301,6 +301,7 @@ fn emit_malformed_attribute(
             | sym::naked
             | sym::no_mangle
             | sym::must_use
+            | sym::track_caller
     ) {
         return;
     }
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 29526817257..06acf7bf992 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -82,11 +82,14 @@ passes_collapse_debuginfo =
 passes_confusables = attribute should be applied to an inherent method
     .label = not an inherent method
 
+passes_const_continue_attr =
+    `#[const_continue]` should be applied to a break expression
+    .label = not a break expression
+
 passes_const_stable_not_stable =
     attribute `#[rustc_const_stable]` can only be applied to functions that are declared `#[stable]`
     .label = attribute specified here
 
-
 passes_coroutine_on_non_closure =
     attribute should be applied to closures
     .label = not a closure
@@ -446,6 +449,10 @@ passes_linkage =
     attribute should be applied to a function or static
     .label = not a function definition or static
 
+passes_loop_match_attr =
+    `#[loop_match]` should be applied to a loop
+    .label = not a loop
+
 passes_macro_export =
     `#[macro_export]` only has an effect on macro definitions
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 25b84179443..99c220d946e 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -138,6 +138,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => {
                     self.check_optimize(hir_id, *attr_span, span, target)
                 }
+                Attribute::Parsed(AttributeKind::LoopMatch(attr_span)) => {
+                    self.check_loop_match(hir_id, *attr_span, target)
+                }
+                Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => {
+                    self.check_const_continue(hir_id, *attr_span, target)
+                }
                 Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
                     .check_allow_internal_unstable(
                         hir_id,
@@ -169,6 +175,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::Naked(attr_span)) => {
                     self.check_naked(hir_id, *attr_span, span, target)
                 }
+                Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => {
+                    self.check_track_caller(hir_id, *attr_span, attrs, span, target)
+                }
                 Attribute::Parsed(
                     AttributeKind::BodyStability { .. }
                     | AttributeKind::ConstStabilityIndirect
@@ -205,9 +214,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             self.check_target_feature(hir_id, attr, span, target, attrs)
                         }
                         [sym::thread_local, ..] => self.check_thread_local(attr, span, target),
-                        [sym::track_caller, ..] => {
-                            self.check_track_caller(hir_id, attr.span(), attrs, span, target)
-                        }
                         [sym::doc, ..] => self.check_doc_attrs(
                             attr,
                             attr_item.style,
@@ -722,9 +728,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             // erroneously allowed it and some crates used it accidentally, to be compatible
             // with crates depending on them, we can't throw an error here.
             Target::Field | Target::Arm | Target::MacroDef => {
-                for attr in attrs {
-                    self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "track_caller");
-                }
+                self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "track_caller");
             }
             _ => {
                 self.dcx().emit_err(errors::TrackedCallerWrongLocation {
@@ -2633,6 +2637,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }
         }
     }
+
+    fn check_loop_match(&self, hir_id: HirId, attr_span: Span, target: Target) {
+        let node_span = self.tcx.hir_span(hir_id);
+
+        if !matches!(target, Target::Expression) {
+            self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });
+            return;
+        }
+
+        if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Loop(..)) {
+            self.dcx().emit_err(errors::LoopMatchAttr { attr_span, node_span });
+        };
+    }
+
+    fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) {
+        let node_span = self.tcx.hir_span(hir_id);
+
+        if !matches!(target, Target::Expression) {
+            self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
+            return;
+        }
+
+        if !matches!(self.tcx.hir_expect_expr(hir_id).kind, hir::ExprKind::Break(..)) {
+            self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span });
+        };
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 94c8ae77ed7..c933647165c 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -31,6 +31,24 @@ pub(crate) struct AutoDiffAttr {
     pub attr_span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(passes_loop_match_attr)]
+pub(crate) struct LoopMatchAttr {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub node_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_const_continue_attr)]
+pub(crate) struct ConstContinueAttr {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub node_span: Span,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(passes_outer_crate_level_attr)]
 pub(crate) struct OuterCrateLevelAttr;
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index f7a4931c111..09685640e50 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -314,7 +314,8 @@ impl IntRange {
         IntRange { lo, hi }
     }
 
-    fn is_subrange(&self, other: &Self) -> bool {
+    #[inline]
+    pub fn is_subrange(&self, other: &Self) -> bool {
         other.lo <= self.lo && self.hi <= other.hi
     }
 
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index a4c6f186222..c0d9937e34d 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -494,6 +494,7 @@ impl RustcInternal for Abi {
             Abi::RustCall => rustc_abi::ExternAbi::RustCall,
             Abi::Unadjusted => rustc_abi::ExternAbi::Unadjusted,
             Abi::RustCold => rustc_abi::ExternAbi::RustCold,
+            Abi::RustInvalid => rustc_abi::ExternAbi::RustInvalid,
             Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
             Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
             Abi::Custom => rustc_abi::ExternAbi::Custom,
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index b4239ddd896..2c652c7546e 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -877,6 +877,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
             ExternAbi::RustCall => Abi::RustCall,
             ExternAbi::Unadjusted => Abi::Unadjusted,
             ExternAbi::RustCold => Abi::RustCold,
+            ExternAbi::RustInvalid => Abi::RustInvalid,
             ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
             ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
             ExternAbi::Custom => Abi::Custom,
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 4415cd6e2e3..1ae79491642 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -1126,6 +1126,7 @@ pub enum Abi {
     RustCold,
     RiscvInterruptM,
     RiscvInterruptS,
+    RustInvalid,
     Custom,
 }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 5861ae4a372..c9262d24a17 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -693,6 +693,7 @@ symbols! {
         const_closures,
         const_compare_raw_pointers,
         const_constructor,
+        const_continue,
         const_deallocate,
         const_destruct,
         const_eval_limit,
@@ -1305,6 +1306,7 @@ symbols! {
         logf64,
         loongarch_target_feature,
         loop_break_value,
+        loop_match,
         lt,
         m68k_target_feature,
         macro_at_most_once_rep,
diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs
index 42ec10a8e15..ce1bdcbb8ac 100644
--- a/compiler/rustc_target/src/spec/abi_map.rs
+++ b/compiler/rustc_target/src/spec/abi_map.rs
@@ -156,7 +156,8 @@ impl AbiMap {
                 | ExternAbi::Msp430Interrupt
                 | ExternAbi::RiscvInterruptM
                 | ExternAbi::RiscvInterruptS
-                | ExternAbi::X86Interrupt,
+                | ExternAbi::X86Interrupt
+                | ExternAbi::RustInvalid,
                 _,
             ) => return AbiMapping::Invalid,
         };
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index b275cd382ab..60f8bd9d83a 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -226,7 +226,11 @@ fn recurse_build<'tcx>(
         ExprKind::Yield { .. } => {
             error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
         }
-        ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
+        ExprKind::Continue { .. }
+        | ExprKind::ConstContinue { .. }
+        | ExprKind::Break { .. }
+        | ExprKind::Loop { .. }
+        | ExprKind::LoopMatch { .. } => {
             error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
         }
         ExprKind::Box { .. } => error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?,
@@ -329,6 +333,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
             | thir::ExprKind::NeverToAny { .. }
             | thir::ExprKind::PointerCoercion { .. }
             | thir::ExprKind::Loop { .. }
+            | thir::ExprKind::LoopMatch { .. }
             | thir::ExprKind::Let { .. }
             | thir::ExprKind::Match { .. }
             | thir::ExprKind::Block { .. }
@@ -342,6 +347,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
             | thir::ExprKind::RawBorrow { .. }
             | thir::ExprKind::Break { .. }
             | thir::ExprKind::Continue { .. }
+            | thir::ExprKind::ConstContinue { .. }
             | thir::ExprKind::Return { .. }
             | thir::ExprKind::Become { .. }
             | thir::ExprKind::Array { .. }
diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index 1d2e13db218..881a7a24083 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -467,7 +467,7 @@ impl CStr {
     /// // 💀 this violates `CStr::from_ptr`'s safety contract
     /// // 💀 leading to a dereference of a dangling pointer,
     /// // 💀 which is immediate undefined behavior.
-    /// // 💀 *BOOM*, you're dead, you're entire program has no meaning.
+    /// // 💀 *BOOM*, you're dead, your entire program has no meaning.
     /// dbg!(unsafe { CStr::from_ptr(ptr) });
     /// ```
     ///
diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs
index a95709b4891..9b674a25165 100644
--- a/library/std/src/sys/fs/windows.rs
+++ b/library/std/src/sys/fs/windows.rs
@@ -132,6 +132,7 @@ impl Iterator for ReadDir {
             let mut wfd = mem::zeroed();
             loop {
                 if c::FindNextFileW(handle.0, &mut wfd) == 0 {
+                    self.handle = None;
                     match api::get_last_error() {
                         WinError::NO_MORE_FILES => return None,
                         WinError { code } => {
diff --git a/src/doc/unstable-book/src/compiler-flags/macro-stats.md b/src/doc/unstable-book/src/compiler-flags/macro-stats.md
index b2622cff057..f3fa69058a7 100644
--- a/src/doc/unstable-book/src/compiler-flags/macro-stats.md
+++ b/src/doc/unstable-book/src/compiler-flags/macro-stats.md
@@ -10,12 +10,12 @@ generated code is normally invisible to the programmer.
 
 This flag helps identify such cases. When enabled, the compiler measures the
 effect on code size of all used macros and prints a table summarizing that
-effect. For each distinct macro, it counts how many times it is used, and the
-net effect on code size (in terms of lines of code, and bytes of code). The
+effect. For each distinct macro, it counts how many times it is used, and how
+much code it produces when expanded (in lines of code, and bytes of code). The
 code size evaluation uses the compiler's internal pretty-printing, and so will
 be independent of the formatting in the original code.
 
-Note that the net effect of a macro may be negative. E.g. the `cfg!` and
+Note that the output size of a macro may be zero. E.g. the `cfg!` and
 `#[test]` macros often strip out code.
 
 If a macro is identified as causing a large increase in code size, it is worth
diff --git a/src/doc/unstable-book/src/language-features/loop-match.md b/src/doc/unstable-book/src/language-features/loop-match.md
new file mode 100644
index 00000000000..4cc763d3434
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/loop-match.md
@@ -0,0 +1,52 @@
+# `loop_match`
+
+The tracking issue for this feature is: [#132306]
+
+[#132306]: https://github.com/rust-lang/rust/issues/132306
+
+------
+
+The `#[loop_match]` and `#[const_continue]` attributes can be used to improve the code
+generation of logic that fits this shape:
+
+```ignore (pseudo-rust)
+loop {
+    state = 'blk: {
+        match state {
+            State::A => {
+                break 'blk State::B
+            }
+            State::B => { /* ... */ }
+            /* ... */
+        }
+    }
+}
+```
+
+Here the loop itself can be annotated with `#[loop_match]`, and any `break 'blk` with
+`#[const_continue]` if the value is know at compile time:
+
+```ignore (pseudo-rust)
+#[loop_match]
+loop {
+    state = 'blk: {
+        match state {
+            State::A => {
+                #[const_continue]
+                break 'blk State::B
+            }
+            State::B => { /* ... */ }
+            /* ... */
+        }
+    }
+}
+```
+
+The observable behavior of this loop is exactly the same as without the extra attributes.
+The difference is in the generated output: normally, when the state is `A`, control flow
+moves from the `A` branch, back to the top of the loop, then to the `B` branch. With the
+attributes, The `A` branch will immediately jump to the `B` branch.
+
+Removing the indirection can be beneficial for stack usage and branch prediction, and
+enables other optimizations by clearly splitting out the control flow paths that your
+program will actually use.
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 515424cbef1..e16acc9622f 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -469,7 +469,8 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
 
                     let unsafety_flag = match myitem.kind {
                         clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
-                            if myitem.fn_header(tcx).unwrap().is_unsafe() =>
+                            if myitem.fn_header(tcx).unwrap().safety
+                                == hir::HeaderSafety::Normal(hir::Safety::Unsafe) =>
                         {
                             "<sup title=\"unsafe function\">⚠</sup>"
                         }
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 84709f085062cbf3c51fa507527c1b233401517
+Subproject 409fed7dc1553d49cb9a8c0637d12d65571346c
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 6ed7c87915b..b0077a9b05f 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -7,6 +7,7 @@ use clippy_utils::{
     get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
 };
 use rustc_abi::ExternAbi;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_errors::Applicability;
 use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -155,7 +156,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
             let sig = match callee_ty_adjusted.kind() {
                 ty::FnDef(def, _) => {
                     // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
-                    if cx.tcx.has_attr(*def, sym::track_caller) {
+                    if find_attr!(cx.tcx.get_all_attrs(*def), AttributeKind::TrackCaller(..)) {
                         return;
                     }
 
@@ -236,7 +237,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
         },
         ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
             if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
-                && !cx.tcx.has_attr(method_def_id, sym::track_caller)
+                && !find_attr!(cx.tcx.get_all_attrs(method_def_id), AttributeKind::TrackCaller(..))
                 && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
             {
                 let mut app = Applicability::MachineApplicable;
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index be6b483bfff..08aedff2b20 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -2289,8 +2289,10 @@ fn rewrite_expr_addrof(
 ) -> RewriteResult {
     let operator_str = match (mutability, borrow_kind) {
         (ast::Mutability::Not, ast::BorrowKind::Ref) => "&",
+        (ast::Mutability::Not, ast::BorrowKind::Pin) => "&pin const ",
         (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ",
         (ast::Mutability::Mut, ast::BorrowKind::Ref) => "&mut ",
+        (ast::Mutability::Mut, ast::BorrowKind::Pin) => "&pin mut ",
         (ast::Mutability::Mut, ast::BorrowKind::Raw) => "&raw mut ",
     };
     rewrite_unary_prefix(context, operator_str, expr, shape)
diff --git a/src/tools/rustfmt/tests/source/pin_sugar.rs b/src/tools/rustfmt/tests/source/pin_sugar.rs
index 370dfbc196a..e5b47339b92 100644
--- a/src/tools/rustfmt/tests/source/pin_sugar.rs
+++ b/src/tools/rustfmt/tests/source/pin_sugar.rs
@@ -18,3 +18,13 @@ impl Foo {
 mut self) {}
     fn i(&pin      mut   self) {}
 }
+
+fn borrows() {
+    let mut foo = 0_i32;
+    let x: Pin<&mut _> = & pin 
+    mut    foo;
+
+    let x: Pin<&_> = &
+    pin                const 
+    foo;
+}
diff --git a/src/tools/rustfmt/tests/target/pin_sugar.rs b/src/tools/rustfmt/tests/target/pin_sugar.rs
index 7d04efb1b32..09ad23a5807 100644
--- a/src/tools/rustfmt/tests/target/pin_sugar.rs
+++ b/src/tools/rustfmt/tests/target/pin_sugar.rs
@@ -16,3 +16,10 @@ impl Foo {
     fn h<'a>(&'a pin mut self) {}
     fn i(&pin mut self) {}
 }
+
+fn borrows() {
+    let mut foo = 0_i32;
+    let x: Pin<&mut _> = &pin mut foo;
+
+    let x: Pin<&_> = &pin const foo;
+}
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 4e0bbcd7c6c..bf813d2131e 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -374,6 +374,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "scoped-tls",
     "scopeguard",
     "self_cell",
+    "semver",
     "serde",
     "serde_derive",
     "serde_json",
diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml
index 642d48b9952..ce718902b29 100644
--- a/src/tools/wasm-component-ld/Cargo.toml
+++ b/src/tools/wasm-component-ld/Cargo.toml
@@ -10,4 +10,4 @@ name = "wasm-component-ld"
 path = "src/main.rs"
 
 [dependencies]
-wasm-component-ld = "0.5.13"
+wasm-component-ld = "0.5.14"
diff --git a/tests/codegen/abi-x86-interrupt.rs b/tests/codegen/abi-x86-interrupt.rs
index 255ccba2c11..9a1ded2c9e3 100644
--- a/tests/codegen/abi-x86-interrupt.rs
+++ b/tests/codegen/abi-x86-interrupt.rs
@@ -13,8 +13,6 @@
 extern crate minicore;
 use minicore::*;
 
-// CHECK: define x86_intrcc i64 @has_x86_interrupt_abi
+// CHECK: define x86_intrcc void @has_x86_interrupt_abi
 #[no_mangle]
-pub extern "x86-interrupt" fn has_x86_interrupt_abi(a: i64) -> i64 {
-    a
-}
+pub extern "x86-interrupt" fn has_x86_interrupt_abi() {}
diff --git a/tests/codegen/transmute-scalar.rs b/tests/codegen/transmute-scalar.rs
index c080259a917..c57ade58c30 100644
--- a/tests/codegen/transmute-scalar.rs
+++ b/tests/codegen/transmute-scalar.rs
@@ -55,3 +55,48 @@ pub fn ptr_to_int(p: *mut u16) -> usize {
 pub fn int_to_ptr(i: usize) -> *mut u16 {
     unsafe { std::mem::transmute(i) }
 }
+
+// This is the one case where signedness matters to transmuting:
+// the LLVM type is `i8` here because of `repr(i8)`,
+// whereas below with the `repr(u8)` it's `i1` in LLVM instead.
+#[repr(i8)]
+pub enum FakeBoolSigned {
+    False = 0,
+    True = 1,
+}
+
+// CHECK-LABEL: define{{.*}}i8 @bool_to_fake_bool_signed(i1 zeroext %b)
+// CHECK: %_0 = zext i1 %b to i8
+// CHECK-NEXT: ret i8 %_0
+#[no_mangle]
+pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned {
+    unsafe { std::mem::transmute(b) }
+}
+
+// CHECK-LABEL: define{{.*}}i1 @fake_bool_signed_to_bool(i8 %b)
+// CHECK: %_0 = trunc nuw i8 %b to i1
+// CHECK-NEXT: ret i1 %_0
+#[no_mangle]
+pub fn fake_bool_signed_to_bool(b: FakeBoolSigned) -> bool {
+    unsafe { std::mem::transmute(b) }
+}
+
+#[repr(u8)]
+pub enum FakeBoolUnsigned {
+    False = 0,
+    True = 1,
+}
+
+// CHECK-LABEL: define{{.*}}i1 @bool_to_fake_bool_unsigned(i1 zeroext %b)
+// CHECK: ret i1 %b
+#[no_mangle]
+pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned {
+    unsafe { std::mem::transmute(b) }
+}
+
+// CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1 zeroext %b)
+// CHECK: ret i1 %b
+#[no_mangle]
+pub fn fake_bool_unsigned_to_bool(b: FakeBoolUnsigned) -> bool {
+    unsafe { std::mem::transmute(b) }
+}
diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp
new file mode 100644
index 00000000000..212e0e174da
--- /dev/null
+++ b/tests/pretty/pin-ergonomics-hir.pp
@@ -0,0 +1,44 @@
+//@ pretty-compare-only
+//@ pretty-mode:hir
+//@ pp-exact:pin-ergonomics-hir.pp
+
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+#[prelude_import]
+use ::std::prelude::rust_2015::*;
+#[macro_use]
+extern crate std;
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn baz(&mut self) { }
+
+    fn baz_const(&self) { }
+
+    fn baz_lt<'a>(&mut self) { }
+
+    fn baz_const_lt(&self) { }
+}
+
+fn foo(_: Pin<&'_ mut Foo>) { }
+fn foo_lt<'a>(_: Pin<&'a mut Foo>) { }
+
+fn foo_const(_: Pin<&'_ Foo>) { }
+fn foo_const_lt(_: Pin<&'_ Foo>) { }
+
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x);
+
+    let x: Pin<&_> = &pin const Foo;
+
+    foo_const(x);
+    foo_const(x);
+}
+
+fn main() { }
diff --git a/tests/pretty/pin-ergonomics-hir.rs b/tests/pretty/pin-ergonomics-hir.rs
new file mode 100644
index 00000000000..5f2158258f0
--- /dev/null
+++ b/tests/pretty/pin-ergonomics-hir.rs
@@ -0,0 +1,40 @@
+//@ pretty-compare-only
+//@ pretty-mode:hir
+//@ pp-exact:pin-ergonomics-hir.pp
+
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn baz(&mut self) { }
+
+    fn baz_const(&self) { }
+
+    fn baz_lt<'a>(&mut self) { }
+
+    fn baz_const_lt(&self) { }
+}
+
+fn foo(_: Pin<&'_ mut Foo>) { }
+fn foo_lt<'a>(_: Pin<&'a mut Foo>) { }
+
+fn foo_const(_: Pin<&'_ Foo>) { }
+fn foo_const_lt(_: Pin<&'_ Foo>) { }
+
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x);
+
+    let x: Pin<&_> = &pin const Foo;
+
+    foo_const(x);
+    foo_const(x);
+}
+
+fn main() { }
diff --git a/tests/pretty/pin-ergonomics.rs b/tests/pretty/pin-ergonomics.rs
index 47ffc97b118..8e8ced791b1 100644
--- a/tests/pretty/pin-ergonomics.rs
+++ b/tests/pretty/pin-ergonomics.rs
@@ -3,6 +3,8 @@
 #![feature(pin_ergonomics)]
 #![allow(dead_code, incomplete_features)]
 
+use std::pin::Pin;
+
 struct Foo;
 
 impl Foo {
@@ -21,4 +23,15 @@ fn foo_lt<'a>(_: &'a pin mut Foo) {}
 fn foo_const(_: &pin const Foo) {}
 fn foo_const_lt(_: &'_ pin const Foo) {}
 
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x);
+
+    let x: Pin<&_> = &pin const Foo;
+    foo_const(x);
+    foo_const(x);
+}
+
 fn main() {}
diff --git a/tests/run-make/bin-emit-no-symbols/app.rs b/tests/run-make/bin-emit-no-symbols/app.rs
index e9dc1e9744f..ad74fcc43dc 100644
--- a/tests/run-make/bin-emit-no-symbols/app.rs
+++ b/tests/run-make/bin-emit-no-symbols/app.rs
@@ -12,7 +12,15 @@ fn panic(_: &PanicInfo) -> ! {
 }
 
 #[lang = "eh_personality"]
-fn eh() {}
+fn eh(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
 
 #[alloc_error_handler]
 fn oom(_: Layout) -> ! {
diff --git a/tests/run-make/crate-circular-deps-link/a.rs b/tests/run-make/crate-circular-deps-link/a.rs
index a54f429550e..99b2b65049d 100644
--- a/tests/run-make/crate-circular-deps-link/a.rs
+++ b/tests/run-make/crate-circular-deps-link/a.rs
@@ -21,6 +21,12 @@ extern "C" fn __rust_foreign_exception() -> ! {
 }
 
 #[lang = "eh_personality"]
-fn eh_personality() {
+fn eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
     loop {}
 }
diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs
index 344b880faab..26d03fd2d70 100644
--- a/tests/run-make/linker-warning/rmake.rs
+++ b/tests/run-make/linker-warning/rmake.rs
@@ -57,7 +57,8 @@ fn main() {
         diff()
             .expected_file("short-error.txt")
             .actual_text("(linker error)", out.stderr())
-            .normalize(r#"/rustc[^/]*/"#, "/rustc/")
+            .normalize(r#"/rustc[^/_-]*/"#, "/rustc/")
+            .normalize("libpanic_abort", "libpanic_unwind")
             .normalize(
                 regex::escape(run_make_support::build_root().to_str().unwrap()),
                 "/build-root",
diff --git a/tests/run-make/no-alloc-shim/foo.rs b/tests/run-make/no-alloc-shim/foo.rs
index b5d0d394d2b..a22307f41b3 100644
--- a/tests/run-make/no-alloc-shim/foo.rs
+++ b/tests/run-make/no-alloc-shim/foo.rs
@@ -12,7 +12,13 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! {
 }
 
 #[no_mangle]
-extern "C" fn rust_eh_personality() {
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
     loop {}
 }
 
diff --git a/tests/run-make/short-ice/rmake.rs b/tests/run-make/short-ice/rmake.rs
index 8377954f467..483def62fc7 100644
--- a/tests/run-make/short-ice/rmake.rs
+++ b/tests/run-make/short-ice/rmake.rs
@@ -5,8 +5,9 @@
 // See https://github.com/rust-lang/rust/issues/107910
 
 //@ needs-target-std
-//@ ignore-windows
-// Reason: the assert_eq! on line 32 fails, as error output on Windows is different.
+//@ ignore-i686-pc-windows-msvc
+// Reason: the assert_eq! on line 37 fails, almost seems like it missing debug info?
+// Haven't been able to reproduce locally, but it happens on CI.
 
 use run_make_support::rustc;
 
@@ -29,10 +30,16 @@ fn main() {
 
     let rustc_query_count_full = count_lines_with(rust_test_log_2, "rustc_query_");
 
-    assert!(rust_test_log_1.lines().count() < rust_test_log_2.lines().count());
+    assert!(
+        rust_test_log_1.lines().count() < rust_test_log_2.lines().count(),
+        "Short backtrace should be shorter than full backtrace.\nShort backtrace:\n\
+        {rust_test_log_1}\nFull backtrace:\n{rust_test_log_2}"
+    );
     assert_eq!(
         count_lines_with(rust_test_log_2, "__rust_begin_short_backtrace"),
-        count_lines_with(rust_test_log_2, "__rust_end_short_backtrace")
+        count_lines_with(rust_test_log_2, "__rust_end_short_backtrace"),
+        "Full backtrace should contain the short backtrace markers.\nFull backtrace:\n\
+        {rust_test_log_2}"
     );
     assert!(count_lines_with(rust_test_log_1, "rustc_query_") + 5 < rustc_query_count_full);
     assert!(rustc_query_count_full > 5);
diff --git a/tests/rustdoc/target-feature.rs b/tests/rustdoc/target-feature.rs
new file mode 100644
index 00000000000..59a08a0ca94
--- /dev/null
+++ b/tests/rustdoc/target-feature.rs
@@ -0,0 +1,38 @@
+#![crate_name = "foo"]
+
+//@ has 'foo/index.html'
+
+//@ has   - '//dl[@class="item-table"]/dt[1]//a' 'f1_safe'
+//@ has   - '//dl[@class="item-table"]/dt[1]//code' 'popcnt'
+//@ count - '//dl[@class="item-table"]/dt[1]//sup' 0
+//@ has   - '//dl[@class="item-table"]/dt[2]//a' 'f2_not_safe'
+//@ has   - '//dl[@class="item-table"]/dt[2]//code' 'avx2'
+//@ count - '//dl[@class="item-table"]/dt[2]//sup' 1
+//@ has   - '//dl[@class="item-table"]/dt[2]//sup' '⚠'
+
+#[target_feature(enable = "popcnt")]
+//@ has 'foo/fn.f1_safe.html'
+//@ matches - '//pre[@class="rust item-decl"]' '^pub fn f1_safe'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available with target feature popcnt only.'
+pub fn f1_safe() {}
+
+//@ has 'foo/fn.f2_not_safe.html'
+//@ matches - '//pre[@class="rust item-decl"]' '^pub unsafe fn f2_not_safe()'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available with target feature avx2 only.'
+#[target_feature(enable = "avx2")]
+pub unsafe fn f2_not_safe() {}
+
+//@ has 'foo/fn.f3_multifeatures_in_attr.html'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available on target features popcnt and avx2 only.'
+#[target_feature(enable = "popcnt", enable = "avx2")]
+pub fn f3_multifeatures_in_attr() {}
+
+//@ has 'foo/fn.f4_multi_attrs.html'
+//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
+//        'Available on target features popcnt and avx2 only.'
+#[target_feature(enable = "popcnt")]
+#[target_feature(enable = "avx2")]
+pub fn f4_multi_attrs() {}
diff --git a/tests/ui/abi/bad-custom.rs b/tests/ui/abi/bad-custom.rs
index a3b15f244b7..7c881134ccb 100644
--- a/tests/ui/abi/bad-custom.rs
+++ b/tests/ui/abi/bad-custom.rs
@@ -5,7 +5,7 @@
 
 #[unsafe(naked)]
 extern "custom" fn must_be_unsafe(a: i64) -> i64 {
-    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~^ ERROR functions with the "custom" ABI must be unsafe
     //~| ERROR invalid signature for `extern "custom"` function
     std::arch::naked_asm!("")
 }
@@ -23,7 +23,7 @@ unsafe extern "custom" fn no_return_type() -> i64 {
 }
 
 unsafe extern "custom" fn double(a: i64) -> i64 {
-    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
     //~| ERROR invalid signature for `extern "custom"` function
     unimplemented!()
 }
@@ -32,7 +32,7 @@ struct Thing(i64);
 
 impl Thing {
     unsafe extern "custom" fn is_even(self) -> bool {
-        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
         //~| ERROR invalid signature for `extern "custom"` function
         unimplemented!()
     }
@@ -40,7 +40,7 @@ impl Thing {
 
 trait BitwiseNot {
     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
-        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
         //~| ERROR invalid signature for `extern "custom"` function
         unimplemented!()
     }
@@ -50,14 +50,14 @@ impl BitwiseNot for Thing {}
 
 trait Negate {
     extern "custom" fn negate(a: i64) -> i64;
-    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~^ ERROR functions with the "custom" ABI must be unsafe
     //~| ERROR invalid signature for `extern "custom"` function
 }
 
 impl Negate for Thing {
     extern "custom" fn negate(a: i64) -> i64 {
-        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
+        //~| ERROR functions with the "custom" ABI must be unsafe
         //~| ERROR invalid signature for `extern "custom"` function
         -a
     }
@@ -68,7 +68,7 @@ unsafe extern "custom" {
     //~^ ERROR invalid signature for `extern "custom"` function
 
     safe fn extern_cannot_be_safe();
-    //~^ ERROR foreign functions with the `"custom"` ABI cannot be safe
+    //~^ ERROR foreign functions with the "custom" ABI cannot be safe
 }
 
 fn caller(f: unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
@@ -95,8 +95,8 @@ const unsafe extern "custom" fn no_const_fn() {
 }
 
 async unsafe extern "custom" fn no_async_fn() {
-    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
-    //~| ERROR functions with the `"custom"` ABI cannot be `async`
+    //~^ ERROR items with the "custom" ABI can only be declared externally or defined via naked functions
+    //~| ERROR functions with the "custom" ABI cannot be `async`
 }
 
 fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn()  {
diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr
index 77cad1b872b..893382875a2 100644
--- a/tests/ui/abi/bad-custom.stderr
+++ b/tests/ui/abi/bad-custom.stderr
@@ -1,4 +1,4 @@
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/bad-custom.rs:7:1
    |
 LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
@@ -15,7 +15,7 @@ error: invalid signature for `extern "custom"` function
 LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
    |                                   ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - extern "custom" fn must_be_unsafe(a: i64) -> i64 {
@@ -28,7 +28,7 @@ error: invalid signature for `extern "custom"` function
 LL | unsafe extern "custom" fn no_parameters(a: i64) {
    |                                         ^^^^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - unsafe extern "custom" fn no_parameters(a: i64) {
@@ -41,7 +41,7 @@ error: invalid signature for `extern "custom"` function
 LL | unsafe extern "custom" fn no_return_type() -> i64 {
    |                                               ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - unsafe extern "custom" fn no_return_type() -> i64 {
@@ -54,7 +54,7 @@ error: invalid signature for `extern "custom"` function
 LL | unsafe extern "custom" fn double(a: i64) -> i64 {
    |                                  ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL - unsafe extern "custom" fn double(a: i64) -> i64 {
@@ -67,7 +67,7 @@ error: invalid signature for `extern "custom"` function
 LL |     unsafe extern "custom" fn is_even(self) -> bool {
    |                                       ^^^^     ^^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     unsafe extern "custom" fn is_even(self) -> bool {
@@ -80,14 +80,14 @@ error: invalid signature for `extern "custom"` function
 LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
    |                                           ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
 LL +     unsafe extern "custom" fn bitwise_not() {
    |
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/bad-custom.rs:52:5
    |
 LL |     extern "custom" fn negate(a: i64) -> i64;
@@ -104,14 +104,14 @@ error: invalid signature for `extern "custom"` function
 LL |     extern "custom" fn negate(a: i64) -> i64;
    |                               ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     extern "custom" fn negate(a: i64) -> i64;
 LL +     extern "custom" fn negate();
    |
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/bad-custom.rs:58:5
    |
 LL |     extern "custom" fn negate(a: i64) -> i64 {
@@ -128,7 +128,7 @@ error: invalid signature for `extern "custom"` function
 LL |     extern "custom" fn negate(a: i64) -> i64 {
    |                               ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     extern "custom" fn negate(a: i64) -> i64 {
@@ -141,14 +141,14 @@ error: invalid signature for `extern "custom"` function
 LL |     fn increment(a: i64) -> i64;
    |                  ^^^^^^     ^^^
    |
-   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+   = note: functions with the "custom" ABI cannot have any parameters or return type
 help: remove the parameters and return type
    |
 LL -     fn increment(a: i64) -> i64;
 LL +     fn increment();
    |
 
-error: foreign functions with the `"custom"` ABI cannot be safe
+error: foreign functions with the "custom" ABI cannot be safe
   --> $DIR/bad-custom.rs:70:5
    |
 LL |     safe fn extern_cannot_be_safe();
@@ -160,7 +160,7 @@ LL -     safe fn extern_cannot_be_safe();
 LL +     fn extern_cannot_be_safe();
    |
 
-error: functions with the `"custom"` ABI cannot be `async`
+error: functions with the "custom" ABI cannot be `async`
   --> $DIR/bad-custom.rs:97:1
    |
 LL | async unsafe extern "custom" fn no_async_fn() {
@@ -172,7 +172,7 @@ LL - async unsafe extern "custom" fn no_async_fn() {
 LL + unsafe extern "custom" fn no_async_fn() {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:97:1
    |
 LL | async unsafe extern "custom" fn no_async_fn() {
@@ -197,7 +197,7 @@ LL |     f
    = note: unsafe function cannot be called generically without an unsafe block
    = note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }`
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:25:1
    |
 LL | unsafe extern "custom" fn double(a: i64) -> i64 {
@@ -209,7 +209,7 @@ LL + #[unsafe(naked)]
 LL | unsafe extern "custom" fn double(a: i64) -> i64 {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:34:5
    |
 LL |     unsafe extern "custom" fn is_even(self) -> bool {
@@ -221,7 +221,7 @@ LL +     #[unsafe(naked)]
 LL |     unsafe extern "custom" fn is_even(self) -> bool {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:42:5
    |
 LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
@@ -233,7 +233,7 @@ LL +     #[unsafe(naked)]
 LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
    |
 
-error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+error: items with the "custom" ABI can only be declared externally or defined via naked functions
   --> $DIR/bad-custom.rs:58:5
    |
 LL |     extern "custom" fn negate(a: i64) -> i64 {
diff --git a/tests/ui/abi/cannot-be-called.avr.stderr b/tests/ui/abi/cannot-be-called.avr.stderr
index a0f7f18f701..1129893cbfa 100644
--- a/tests/ui/abi/cannot-be-called.avr.stderr
+++ b/tests/ui/abi/cannot-be-called.avr.stderr
@@ -1,71 +1,71 @@
 error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:38:8
+  --> $DIR/cannot-be-called.rs:37:8
    |
 LL | extern "msp430-interrupt" fn msp430() {}
    |        ^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:42:8
+  --> $DIR/cannot-be-called.rs:41:8
    |
 LL | extern "riscv-interrupt-m" fn riscv_m() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:44:8
+  --> $DIR/cannot-be-called.rs:43:8
    |
 LL | extern "riscv-interrupt-s" fn riscv_s() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:46:8
+  --> $DIR/cannot-be-called.rs:45:8
    |
 LL | extern "x86-interrupt" fn x86() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:65:25
+  --> $DIR/cannot-be-called.rs:70:25
    |
 LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
    |                         ^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:77:26
+  --> $DIR/cannot-be-called.rs:76:26
    |
 LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:83:26
+  --> $DIR/cannot-be-called.rs:82:26
    |
 LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:89:22
+  --> $DIR/cannot-be-called.rs:88:22
    |
 LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
 error: functions with the "avr-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:53:5
+  --> $DIR/cannot-be-called.rs:50:5
    |
 LL |     avr();
    |     ^^^^^
    |
 note: an `extern "avr-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:53:5
+  --> $DIR/cannot-be-called.rs:50:5
    |
 LL |     avr();
    |     ^^^^^
 
 error: functions with the "avr-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:73:5
+  --> $DIR/cannot-be-called.rs:66:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "avr-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:73:5
+  --> $DIR/cannot-be-called.rs:66:5
    |
 LL |     f()
    |     ^^^
diff --git a/tests/ui/abi/cannot-be-called.i686.stderr b/tests/ui/abi/cannot-be-called.i686.stderr
index f59fdba2f35..024d5e2e93d 100644
--- a/tests/ui/abi/cannot-be-called.i686.stderr
+++ b/tests/ui/abi/cannot-be-called.i686.stderr
@@ -1,71 +1,71 @@
 error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:38:8
+  --> $DIR/cannot-be-called.rs:37:8
    |
 LL | extern "msp430-interrupt" fn msp430() {}
    |        ^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:40:8
+  --> $DIR/cannot-be-called.rs:39:8
    |
 LL | extern "avr-interrupt" fn avr() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:42:8
+  --> $DIR/cannot-be-called.rs:41:8
    |
 LL | extern "riscv-interrupt-m" fn riscv_m() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:44:8
+  --> $DIR/cannot-be-called.rs:43:8
    |
 LL | extern "riscv-interrupt-s" fn riscv_s() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:65:25
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                         ^^^^^^^^^^^^^^^^^^
-
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:71:22
+  --> $DIR/cannot-be-called.rs:64:22
    |
 LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:77:26
+  --> $DIR/cannot-be-called.rs:76:26
    |
 LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:83:26
+  --> $DIR/cannot-be-called.rs:82:26
    |
 LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error: functions with the "x86-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:59:5
+  --> $DIR/cannot-be-called.rs:58:5
    |
 LL |     x86();
    |     ^^^^^
    |
 note: an `extern "x86-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:59:5
+  --> $DIR/cannot-be-called.rs:58:5
    |
 LL |     x86();
    |     ^^^^^
 
 error: functions with the "x86-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:91:5
+  --> $DIR/cannot-be-called.rs:90:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "x86-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:91:5
+  --> $DIR/cannot-be-called.rs:90:5
    |
 LL |     f()
    |     ^^^
diff --git a/tests/ui/abi/cannot-be-called.msp430.stderr b/tests/ui/abi/cannot-be-called.msp430.stderr
index fa339f0dd2a..52d7d792510 100644
--- a/tests/ui/abi/cannot-be-called.msp430.stderr
+++ b/tests/ui/abi/cannot-be-called.msp430.stderr
@@ -1,71 +1,71 @@
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:40:8
+  --> $DIR/cannot-be-called.rs:39:8
    |
 LL | extern "avr-interrupt" fn avr() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:42:8
+  --> $DIR/cannot-be-called.rs:41:8
    |
 LL | extern "riscv-interrupt-m" fn riscv_m() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:44:8
+  --> $DIR/cannot-be-called.rs:43:8
    |
 LL | extern "riscv-interrupt-s" fn riscv_s() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:46:8
+  --> $DIR/cannot-be-called.rs:45:8
    |
 LL | extern "x86-interrupt" fn x86() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:71:22
+  --> $DIR/cannot-be-called.rs:64:22
    |
 LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:77:26
+  --> $DIR/cannot-be-called.rs:76:26
    |
 LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:83:26
+  --> $DIR/cannot-be-called.rs:82:26
    |
 LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:89:22
+  --> $DIR/cannot-be-called.rs:88:22
    |
 LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
 error: functions with the "msp430-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:51:5
+  --> $DIR/cannot-be-called.rs:52:5
    |
 LL |     msp430();
    |     ^^^^^^^^
    |
 note: an `extern "msp430-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:51:5
+  --> $DIR/cannot-be-called.rs:52:5
    |
 LL |     msp430();
    |     ^^^^^^^^
 
 error: functions with the "msp430-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:67:5
+  --> $DIR/cannot-be-called.rs:72:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "msp430-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:67:5
+  --> $DIR/cannot-be-called.rs:72:5
    |
 LL |     f()
    |     ^^^
diff --git a/tests/ui/abi/cannot-be-called.riscv32.stderr b/tests/ui/abi/cannot-be-called.riscv32.stderr
index 040b60c7657..119d93bd58e 100644
--- a/tests/ui/abi/cannot-be-called.riscv32.stderr
+++ b/tests/ui/abi/cannot-be-called.riscv32.stderr
@@ -1,83 +1,83 @@
 error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:38:8
+  --> $DIR/cannot-be-called.rs:37:8
    |
 LL | extern "msp430-interrupt" fn msp430() {}
    |        ^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:40:8
+  --> $DIR/cannot-be-called.rs:39:8
    |
 LL | extern "avr-interrupt" fn avr() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:46:8
+  --> $DIR/cannot-be-called.rs:45:8
    |
 LL | extern "x86-interrupt" fn x86() {}
    |        ^^^^^^^^^^^^^^^
 
-error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:65:25
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                         ^^^^^^^^^^^^^^^^^^
-
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:71:22
+  --> $DIR/cannot-be-called.rs:64:22
    |
 LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:89:22
+  --> $DIR/cannot-be-called.rs:88:22
    |
 LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
 error: functions with the "riscv-interrupt-m" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:55:5
+  --> $DIR/cannot-be-called.rs:54:5
    |
 LL |     riscv_m();
    |     ^^^^^^^^^
    |
 note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:55:5
+  --> $DIR/cannot-be-called.rs:54:5
    |
 LL |     riscv_m();
    |     ^^^^^^^^^
 
 error: functions with the "riscv-interrupt-s" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:57:5
+  --> $DIR/cannot-be-called.rs:56:5
    |
 LL |     riscv_s();
    |     ^^^^^^^^^
    |
 note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:57:5
+  --> $DIR/cannot-be-called.rs:56:5
    |
 LL |     riscv_s();
    |     ^^^^^^^^^
 
 error: functions with the "riscv-interrupt-m" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:79:5
+  --> $DIR/cannot-be-called.rs:78:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:79:5
+  --> $DIR/cannot-be-called.rs:78:5
    |
 LL |     f()
    |     ^^^
 
 error: functions with the "riscv-interrupt-s" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:85:5
+  --> $DIR/cannot-be-called.rs:84:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:85:5
+  --> $DIR/cannot-be-called.rs:84:5
    |
 LL |     f()
    |     ^^^
diff --git a/tests/ui/abi/cannot-be-called.riscv64.stderr b/tests/ui/abi/cannot-be-called.riscv64.stderr
index 040b60c7657..119d93bd58e 100644
--- a/tests/ui/abi/cannot-be-called.riscv64.stderr
+++ b/tests/ui/abi/cannot-be-called.riscv64.stderr
@@ -1,83 +1,83 @@
 error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:38:8
+  --> $DIR/cannot-be-called.rs:37:8
    |
 LL | extern "msp430-interrupt" fn msp430() {}
    |        ^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:40:8
+  --> $DIR/cannot-be-called.rs:39:8
    |
 LL | extern "avr-interrupt" fn avr() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:46:8
+  --> $DIR/cannot-be-called.rs:45:8
    |
 LL | extern "x86-interrupt" fn x86() {}
    |        ^^^^^^^^^^^^^^^
 
-error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:65:25
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                         ^^^^^^^^^^^^^^^^^^
-
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:71:22
+  --> $DIR/cannot-be-called.rs:64:22
    |
 LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
 error[E0570]: "x86-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:89:22
+  --> $DIR/cannot-be-called.rs:88:22
    |
 LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
 error: functions with the "riscv-interrupt-m" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:55:5
+  --> $DIR/cannot-be-called.rs:54:5
    |
 LL |     riscv_m();
    |     ^^^^^^^^^
    |
 note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:55:5
+  --> $DIR/cannot-be-called.rs:54:5
    |
 LL |     riscv_m();
    |     ^^^^^^^^^
 
 error: functions with the "riscv-interrupt-s" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:57:5
+  --> $DIR/cannot-be-called.rs:56:5
    |
 LL |     riscv_s();
    |     ^^^^^^^^^
    |
 note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:57:5
+  --> $DIR/cannot-be-called.rs:56:5
    |
 LL |     riscv_s();
    |     ^^^^^^^^^
 
 error: functions with the "riscv-interrupt-m" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:79:5
+  --> $DIR/cannot-be-called.rs:78:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "riscv-interrupt-m"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:79:5
+  --> $DIR/cannot-be-called.rs:78:5
    |
 LL |     f()
    |     ^^^
 
 error: functions with the "riscv-interrupt-s" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:85:5
+  --> $DIR/cannot-be-called.rs:84:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "riscv-interrupt-s"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:85:5
+  --> $DIR/cannot-be-called.rs:84:5
    |
 LL |     f()
    |     ^^^
diff --git a/tests/ui/abi/cannot-be-called.rs b/tests/ui/abi/cannot-be-called.rs
index 6918c2f56a3..af979d65d33 100644
--- a/tests/ui/abi/cannot-be-called.rs
+++ b/tests/ui/abi/cannot-be-called.rs
@@ -25,9 +25,8 @@ So we test that they error in essentially all of the same places.
     no_core,
     abi_msp430_interrupt,
     abi_avr_interrupt,
-    abi_gpu_kernel,
     abi_x86_interrupt,
-    abi_riscv_interrupt,
+    abi_riscv_interrupt
 )]
 
 extern crate minicore;
@@ -48,10 +47,10 @@ extern "x86-interrupt" fn x86() {}
 
 /* extern "interrupt" calls  */
 fn call_the_interrupts() {
-    msp430();
-    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
     avr();
     //[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be called
+    msp430();
+    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
     riscv_m();
     //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-m" ABI cannot be called
     riscv_s();
@@ -62,18 +61,18 @@ fn call_the_interrupts() {
 
 /* extern "interrupt" fnptr calls */
 
-fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-    //[x64,x64_win,i686,riscv32,riscv64,avr]~^ ERROR is not a supported ABI
-    f()
-    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
-}
-
 fn avr_ptr(f: extern "avr-interrupt" fn()) {
     //[x64,x64_win,i686,riscv32,riscv64,msp430]~^ ERROR is not a supported ABI
     f()
     //[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be called
 }
 
+fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+    //[x64,x64_win,i686,riscv32,riscv64,avr]~^ ERROR is not a supported ABI
+    f()
+    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be called
+}
+
 fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
     //[x64,x64_win,i686,avr,msp430]~^ ERROR is not a supported ABI
     f()
diff --git a/tests/ui/abi/cannot-be-called.x64.stderr b/tests/ui/abi/cannot-be-called.x64.stderr
index f59fdba2f35..024d5e2e93d 100644
--- a/tests/ui/abi/cannot-be-called.x64.stderr
+++ b/tests/ui/abi/cannot-be-called.x64.stderr
@@ -1,71 +1,71 @@
 error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:38:8
+  --> $DIR/cannot-be-called.rs:37:8
    |
 LL | extern "msp430-interrupt" fn msp430() {}
    |        ^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:40:8
+  --> $DIR/cannot-be-called.rs:39:8
    |
 LL | extern "avr-interrupt" fn avr() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:42:8
+  --> $DIR/cannot-be-called.rs:41:8
    |
 LL | extern "riscv-interrupt-m" fn riscv_m() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:44:8
+  --> $DIR/cannot-be-called.rs:43:8
    |
 LL | extern "riscv-interrupt-s" fn riscv_s() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:65:25
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                         ^^^^^^^^^^^^^^^^^^
-
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:71:22
+  --> $DIR/cannot-be-called.rs:64:22
    |
 LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:77:26
+  --> $DIR/cannot-be-called.rs:76:26
    |
 LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:83:26
+  --> $DIR/cannot-be-called.rs:82:26
    |
 LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error: functions with the "x86-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:59:5
+  --> $DIR/cannot-be-called.rs:58:5
    |
 LL |     x86();
    |     ^^^^^
    |
 note: an `extern "x86-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:59:5
+  --> $DIR/cannot-be-called.rs:58:5
    |
 LL |     x86();
    |     ^^^^^
 
 error: functions with the "x86-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:91:5
+  --> $DIR/cannot-be-called.rs:90:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "x86-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:91:5
+  --> $DIR/cannot-be-called.rs:90:5
    |
 LL |     f()
    |     ^^^
diff --git a/tests/ui/abi/cannot-be-called.x64_win.stderr b/tests/ui/abi/cannot-be-called.x64_win.stderr
index f59fdba2f35..024d5e2e93d 100644
--- a/tests/ui/abi/cannot-be-called.x64_win.stderr
+++ b/tests/ui/abi/cannot-be-called.x64_win.stderr
@@ -1,71 +1,71 @@
 error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:38:8
+  --> $DIR/cannot-be-called.rs:37:8
    |
 LL | extern "msp430-interrupt" fn msp430() {}
    |        ^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:40:8
+  --> $DIR/cannot-be-called.rs:39:8
    |
 LL | extern "avr-interrupt" fn avr() {}
    |        ^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:42:8
+  --> $DIR/cannot-be-called.rs:41:8
    |
 LL | extern "riscv-interrupt-m" fn riscv_m() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:44:8
+  --> $DIR/cannot-be-called.rs:43:8
    |
 LL | extern "riscv-interrupt-s" fn riscv_s() {}
    |        ^^^^^^^^^^^^^^^^^^^
 
-error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:65:25
-   |
-LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
-   |                         ^^^^^^^^^^^^^^^^^^
-
 error[E0570]: "avr-interrupt" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:71:22
+  --> $DIR/cannot-be-called.rs:64:22
    |
 LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
    |                      ^^^^^^^^^^^^^^^
 
+error[E0570]: "msp430-interrupt" is not a supported ABI for the current target
+  --> $DIR/cannot-be-called.rs:70:25
+   |
+LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
+   |                         ^^^^^^^^^^^^^^^^^^
+
 error[E0570]: "riscv-interrupt-m" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:77:26
+  --> $DIR/cannot-be-called.rs:76:26
    |
 LL | fn riscv_m_ptr(f: extern "riscv-interrupt-m" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error[E0570]: "riscv-interrupt-s" is not a supported ABI for the current target
-  --> $DIR/cannot-be-called.rs:83:26
+  --> $DIR/cannot-be-called.rs:82:26
    |
 LL | fn riscv_s_ptr(f: extern "riscv-interrupt-s" fn()) {
    |                          ^^^^^^^^^^^^^^^^^^^
 
 error: functions with the "x86-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:59:5
+  --> $DIR/cannot-be-called.rs:58:5
    |
 LL |     x86();
    |     ^^^^^
    |
 note: an `extern "x86-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:59:5
+  --> $DIR/cannot-be-called.rs:58:5
    |
 LL |     x86();
    |     ^^^^^
 
 error: functions with the "x86-interrupt" ABI cannot be called
-  --> $DIR/cannot-be-called.rs:91:5
+  --> $DIR/cannot-be-called.rs:90:5
    |
 LL |     f()
    |     ^^^
    |
 note: an `extern "x86-interrupt"` function can only be called using inline assembly
-  --> $DIR/cannot-be-called.rs:91:5
+  --> $DIR/cannot-be-called.rs:90:5
    |
 LL |     f()
    |     ^^^
diff --git a/tests/ui/abi/cannot-be-coroutine.avr.stderr b/tests/ui/abi/cannot-be-coroutine.avr.stderr
new file mode 100644
index 00000000000..b06da0f3352
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.avr.stderr
@@ -0,0 +1,23 @@
+error: functions with the "avr-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:36:1
+   |
+LL | async extern "avr-interrupt" fn avr() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "avr-interrupt" fn avr() {
+LL + extern "avr-interrupt" fn avr() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.i686.stderr b/tests/ui/abi/cannot-be-coroutine.i686.stderr
new file mode 100644
index 00000000000..cbbddd087c8
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.i686.stderr
@@ -0,0 +1,23 @@
+error: functions with the "x86-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:52:1
+   |
+LL | async extern "x86-interrupt" fn x86() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "x86-interrupt" fn x86() {
+LL + extern "x86-interrupt" fn x86() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.msp430.stderr b/tests/ui/abi/cannot-be-coroutine.msp430.stderr
new file mode 100644
index 00000000000..951ce13b605
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.msp430.stderr
@@ -0,0 +1,23 @@
+error: functions with the "msp430-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:40:1
+   |
+LL | async extern "msp430-interrupt" fn msp430() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "msp430-interrupt" fn msp430() {
+LL + extern "msp430-interrupt" fn msp430() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.riscv32.stderr b/tests/ui/abi/cannot-be-coroutine.riscv32.stderr
new file mode 100644
index 00000000000..8e3b3a2940a
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.riscv32.stderr
@@ -0,0 +1,35 @@
+error: functions with the "riscv-interrupt-m" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:44:1
+   |
+LL | async extern "riscv-interrupt-m" fn riscv_m() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-m" fn riscv_m() {
+LL + extern "riscv-interrupt-m" fn riscv_m() {
+   |
+
+error: functions with the "riscv-interrupt-s" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:48:1
+   |
+LL | async extern "riscv-interrupt-s" fn riscv_s() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-s" fn riscv_s() {
+LL + extern "riscv-interrupt-s" fn riscv_s() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.riscv64.stderr b/tests/ui/abi/cannot-be-coroutine.riscv64.stderr
new file mode 100644
index 00000000000..8e3b3a2940a
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.riscv64.stderr
@@ -0,0 +1,35 @@
+error: functions with the "riscv-interrupt-m" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:44:1
+   |
+LL | async extern "riscv-interrupt-m" fn riscv_m() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-m" fn riscv_m() {
+LL + extern "riscv-interrupt-m" fn riscv_m() {
+   |
+
+error: functions with the "riscv-interrupt-s" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:48:1
+   |
+LL | async extern "riscv-interrupt-s" fn riscv_s() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "riscv-interrupt-s" fn riscv_s() {
+LL + extern "riscv-interrupt-s" fn riscv_s() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.rs b/tests/ui/abi/cannot-be-coroutine.rs
new file mode 100644
index 00000000000..7270a55f69e
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.rs
@@ -0,0 +1,54 @@
+//@ add-core-stubs
+//@ edition: 2021
+//@ revisions: x64 x64_win i686 riscv32 riscv64 avr msp430
+//
+//@ [x64] needs-llvm-components: x86
+//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
+//@ [x64_win] needs-llvm-components: x86
+//@ [x64_win] compile-flags: --target=x86_64-pc-windows-msvc --crate-type=rlib
+//@ [i686] needs-llvm-components: x86
+//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib
+//@ [riscv32] needs-llvm-components: riscv
+//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib
+//@ [riscv64] needs-llvm-components: riscv
+//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib
+//@ [avr] needs-llvm-components: avr
+//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib
+//@ [msp430] needs-llvm-components: msp430
+//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib
+#![no_core]
+#![feature(
+    no_core,
+    abi_msp430_interrupt,
+    abi_avr_interrupt,
+    abi_x86_interrupt,
+    abi_riscv_interrupt
+)]
+
+extern crate minicore;
+use minicore::*;
+
+// We ignore this error; implementing all of the async-related lang items is not worth it.
+async fn vanilla(){
+    //~^ ERROR requires `ResumeTy` lang_item
+}
+
+async extern "avr-interrupt" fn avr() {
+    //[avr]~^ ERROR functions with the "avr-interrupt" ABI cannot be `async`
+}
+
+async extern "msp430-interrupt" fn msp430() {
+    //[msp430]~^ ERROR functions with the "msp430-interrupt" ABI cannot be `async`
+}
+
+async extern "riscv-interrupt-m" fn riscv_m() {
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-m" ABI cannot be `async`
+}
+
+async extern "riscv-interrupt-s" fn riscv_s() {
+    //[riscv32,riscv64]~^ ERROR functions with the "riscv-interrupt-s" ABI cannot be `async`
+}
+
+async extern "x86-interrupt" fn x86() {
+    //[x64,x64_win,i686]~^ ERROR functions with the "x86-interrupt" ABI cannot be `async`
+}
diff --git a/tests/ui/abi/cannot-be-coroutine.x64.stderr b/tests/ui/abi/cannot-be-coroutine.x64.stderr
new file mode 100644
index 00000000000..cbbddd087c8
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.x64.stderr
@@ -0,0 +1,23 @@
+error: functions with the "x86-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:52:1
+   |
+LL | async extern "x86-interrupt" fn x86() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "x86-interrupt" fn x86() {
+LL + extern "x86-interrupt" fn x86() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/cannot-be-coroutine.x64_win.stderr b/tests/ui/abi/cannot-be-coroutine.x64_win.stderr
new file mode 100644
index 00000000000..cbbddd087c8
--- /dev/null
+++ b/tests/ui/abi/cannot-be-coroutine.x64_win.stderr
@@ -0,0 +1,23 @@
+error: functions with the "x86-interrupt" ABI cannot be `async`
+  --> $DIR/cannot-be-coroutine.rs:52:1
+   |
+LL | async extern "x86-interrupt" fn x86() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async extern "x86-interrupt" fn x86() {
+LL + extern "x86-interrupt" fn x86() {
+   |
+
+error: requires `ResumeTy` lang_item
+  --> $DIR/cannot-be-coroutine.rs:32:19
+   |
+LL |   async fn vanilla(){
+   |  ___________________^
+LL | |
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/abi/invalid-call-abi-ctfe.rs b/tests/ui/abi/invalid-call-abi-ctfe.rs
new file mode 100644
index 00000000000..343cc728fe3
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi-ctfe.rs
@@ -0,0 +1,14 @@
+// Fix for #142969 where an invalid ABI in a signature still had its call ABI computed
+// because CTFE tried to evaluate it, despite previous errors during AST-to-HIR lowering.
+
+#![feature(rustc_attrs)]
+
+const extern "rust-invalid" fn foo() {
+    //~^ ERROR "rust-invalid" is not a supported ABI for the current target
+    panic!()
+}
+
+const _: () = foo();
+
+
+fn main() {}
diff --git a/tests/ui/abi/invalid-call-abi-ctfe.stderr b/tests/ui/abi/invalid-call-abi-ctfe.stderr
new file mode 100644
index 00000000000..402de4b69b9
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi-ctfe.stderr
@@ -0,0 +1,9 @@
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/invalid-call-abi-ctfe.rs:6:14
+   |
+LL | const extern "rust-invalid" fn foo() {
+   |              ^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/abi/invalid-call-abi.rs b/tests/ui/abi/invalid-call-abi.rs
new file mode 100644
index 00000000000..076ddd91ab0
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi.rs
@@ -0,0 +1,12 @@
+// Tests the `"rustc-invalid"` ABI, which is never canonizable.
+
+#![feature(rustc_attrs)]
+
+const extern "rust-invalid" fn foo() {
+    //~^ ERROR "rust-invalid" is not a supported ABI for the current target
+    panic!()
+}
+
+fn main() {
+    foo();
+}
diff --git a/tests/ui/abi/invalid-call-abi.stderr b/tests/ui/abi/invalid-call-abi.stderr
new file mode 100644
index 00000000000..c4a90158dcf
--- /dev/null
+++ b/tests/ui/abi/invalid-call-abi.stderr
@@ -0,0 +1,9 @@
+error[E0570]: "rust-invalid" is not a supported ABI for the current target
+  --> $DIR/invalid-call-abi.rs:5:14
+   |
+LL | const extern "rust-invalid" fn foo() {
+   |              ^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0570`.
diff --git a/tests/ui/allocator/no_std-alloc-error-handler-custom.rs b/tests/ui/allocator/no_std-alloc-error-handler-custom.rs
index 6bbfb72510d..1b0f0608fc6 100644
--- a/tests/ui/allocator/no_std-alloc-error-handler-custom.rs
+++ b/tests/ui/allocator/no_std-alloc-error-handler-custom.rs
@@ -6,13 +6,14 @@
 //@ compile-flags:-C panic=abort
 //@ aux-build:helper.rs
 
-#![feature(rustc_private, lang_items)]
+#![feature(rustc_private, lang_items, panic_unwind)]
 #![feature(alloc_error_handler)]
 #![no_std]
 #![no_main]
 
 extern crate alloc;
 extern crate libc;
+extern crate unwind; // For _Unwind_Resume
 
 // ARM targets need these symbols
 #[no_mangle]
@@ -70,7 +71,15 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
 // in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
 // unwind. So, for this test case we will define the symbol.
 #[lang = "eh_personality"]
-extern "C" fn rust_eh_personality() {}
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
 
 #[derive(Default, Debug)]
 struct Page(#[allow(dead_code)] [[u64; 32]; 16]);
diff --git a/tests/ui/allocator/no_std-alloc-error-handler-default.rs b/tests/ui/allocator/no_std-alloc-error-handler-default.rs
index 8bcf054ac85..51ecf1a6731 100644
--- a/tests/ui/allocator/no_std-alloc-error-handler-default.rs
+++ b/tests/ui/allocator/no_std-alloc-error-handler-default.rs
@@ -6,12 +6,13 @@
 //@ compile-flags:-C panic=abort
 //@ aux-build:helper.rs
 
-#![feature(rustc_private, lang_items)]
+#![feature(rustc_private, lang_items, panic_unwind)]
 #![no_std]
 #![no_main]
 
 extern crate alloc;
 extern crate libc;
+extern crate unwind; // For _Unwind_Resume
 
 // ARM targets need these symbols
 #[no_mangle]
@@ -57,7 +58,15 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! {
 // in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
 // unwind. So, for this test case we will define the symbol.
 #[lang = "eh_personality"]
-extern "C" fn rust_eh_personality() {}
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
 
 #[derive(Default, Debug)]
 struct Page(#[allow(dead_code)] [[u64; 32]; 16]);
diff --git a/tests/ui/extern-flag/auxiliary/panic_handler.rs b/tests/ui/extern-flag/auxiliary/panic_handler.rs
index 9140ceed229..9607f0ed013 100644
--- a/tests/ui/extern-flag/auxiliary/panic_handler.rs
+++ b/tests/ui/extern-flag/auxiliary/panic_handler.rs
@@ -12,4 +12,12 @@ pub fn begin_panic_handler(_info: &core::panic::PanicInfo<'_>) -> ! {
 }
 
 #[lang = "eh_personality"]
-extern "C" fn eh_personality() {}
+extern "C" fn eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.rs b/tests/ui/feature-gates/feature-gate-abi-custom.rs
index 3ddce974dd7..312b6230b74 100644
--- a/tests/ui/feature-gates/feature-gate-abi-custom.rs
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.rs
@@ -15,11 +15,11 @@ unsafe extern "custom" fn f7() {
 trait Tr {
     extern "custom" fn m7();
     //~^ ERROR "custom" ABI is experimental
-    //~| ERROR functions with the `"custom"` ABI must be unsafe
+    //~| ERROR functions with the "custom" ABI must be unsafe
     #[unsafe(naked)]
     extern "custom" fn dm7() {
         //~^ ERROR "custom" ABI is experimental
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR functions with the "custom" ABI must be unsafe
         naked_asm!("")
     }
 }
@@ -31,7 +31,7 @@ impl Tr for S {
     #[unsafe(naked)]
     extern "custom" fn m7() {
         //~^ ERROR "custom" ABI is experimental
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR functions with the "custom" ABI must be unsafe
         naked_asm!("")
     }
 }
@@ -41,7 +41,7 @@ impl S {
     #[unsafe(naked)]
     extern "custom" fn im7() {
         //~^ ERROR "custom" ABI is experimental
-        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR functions with the "custom" ABI must be unsafe
         naked_asm!("")
     }
 }
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.stderr b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
index e6dce0126d6..e359dbb5ebe 100644
--- a/tests/ui/feature-gates/feature-gate-abi-custom.stderr
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
@@ -1,4 +1,4 @@
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:16:5
    |
 LL |     extern "custom" fn m7();
@@ -9,7 +9,7 @@ help: add the `unsafe` keyword to this definition
 LL |     unsafe extern "custom" fn m7();
    |     ++++++
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:20:5
    |
 LL |     extern "custom" fn dm7() {
@@ -20,7 +20,7 @@ help: add the `unsafe` keyword to this definition
 LL |     unsafe extern "custom" fn dm7() {
    |     ++++++
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:32:5
    |
 LL |     extern "custom" fn m7() {
@@ -31,7 +31,7 @@ help: add the `unsafe` keyword to this definition
 LL |     unsafe extern "custom" fn m7() {
    |     ++++++
 
-error: functions with the `"custom"` ABI must be unsafe
+error: functions with the "custom" ABI must be unsafe
   --> $DIR/feature-gate-abi-custom.rs:42:5
    |
 LL |     extern "custom" fn im7() {
diff --git a/tests/ui/feature-gates/feature-gate-loop-match.rs b/tests/ui/feature-gates/feature-gate-loop-match.rs
new file mode 100644
index 00000000000..399b20234f3
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-loop-match.rs
@@ -0,0 +1,30 @@
+// Test that `#[loop_match]` and `#[const_continue]` cannot be used without
+// `#![feature(loop_match)]`.
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match] //~ ERROR the `#[loop_match]` attribute is an experimental feature
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    //~^ ERROR the `#[const_continue]` attribute is an experimental feature
+                    break 'blk State::B;
+                }
+                State::B => {
+                    #[const_continue]
+                    //~^ ERROR the `#[const_continue]` attribute is an experimental feature
+                    break 'blk State::C;
+                }
+                State::C => break 'a,
+            }
+        };
+    }
+}
diff --git a/tests/ui/feature-gates/feature-gate-loop-match.stderr b/tests/ui/feature-gates/feature-gate-loop-match.stderr
new file mode 100644
index 00000000000..9b12047cf4d
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-loop-match.stderr
@@ -0,0 +1,33 @@
+error[E0658]: the `#[loop_match]` attribute is an experimental feature
+  --> $DIR/feature-gate-loop-match.rs:12:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+   |
+   = note: see issue #132306 <https://github.com/rust-lang/rust/issues/132306> for more information
+   = help: add `#![feature(loop_match)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the `#[const_continue]` attribute is an experimental feature
+  --> $DIR/feature-gate-loop-match.rs:17:21
+   |
+LL |                     #[const_continue]
+   |                     ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #132306 <https://github.com/rust-lang/rust/issues/132306> for more information
+   = help: add `#![feature(loop_match)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the `#[const_continue]` attribute is an experimental feature
+  --> $DIR/feature-gate-loop-match.rs:22:21
+   |
+LL |                     #[const_continue]
+   |                     ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #132306 <https://github.com/rust-lang/rust/issues/132306> for more information
+   = help: add `#![feature(loop_match)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
index 663a83a665c..7746654555d 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
@@ -17,6 +17,10 @@ fn foo(mut x: Pin<&mut Foo>) {
     let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental
 }
 
+fn foo_const(x: Pin<&Foo>) {
+    let _y: &pin const Foo = x; //~ ERROR pinned reference syntax is experimental
+}
+
 fn foo_sugar(_: &pin mut Foo) {} //~ ERROR pinned reference syntax is experimental
 
 fn bar(x: Pin<&mut Foo>) {
@@ -31,6 +35,18 @@ fn baz(mut x: Pin<&mut Foo>) {
 
 fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental
 
+fn borrows() {
+    let mut x: Pin<&mut _> = &pin mut Foo; //~ ERROR pinned reference syntax is experimental
+    foo(x.as_mut());
+    foo(x.as_mut());
+    foo_const(x.as_ref());
+
+    let x: Pin<&_> = &pin const Foo; //~ ERROR pinned reference syntax is experimental
+
+    foo_const(x);
+    foo_const(x);
+}
+
 #[cfg(any())]
 mod not_compiled {
     use std::pin::Pin;
@@ -63,6 +79,18 @@ mod not_compiled {
     }
 
     fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental
+
+    fn borrows() {
+        let mut x: Pin<&mut _> = &pin mut Foo; //~ ERROR pinned reference syntax is experimental
+        foo(x.as_mut());
+        foo(x.as_mut());
+        foo_const(x.as_ref());
+
+        let x: Pin<&_> = &pin const Foo; //~ ERROR pinned reference syntax is experimental
+
+        foo_const(x);
+        foo_const(x);
+    }
 }
 
 fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
index 8ed7543d86e..a8890254fac 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
@@ -29,7 +29,17 @@ LL |     let _y: &pin mut Foo = x;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:20:18
+  --> $DIR/feature-gate-pin_ergonomics.rs:21:14
+   |
+LL |     let _y: &pin const Foo = x;
+   |              ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:24:18
    |
 LL | fn foo_sugar(_: &pin mut Foo) {}
    |                  ^^^
@@ -39,7 +49,7 @@ LL | fn foo_sugar(_: &pin mut Foo) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:32:18
+  --> $DIR/feature-gate-pin_ergonomics.rs:36:18
    |
 LL | fn baz_sugar(_: &pin const Foo) {}
    |                  ^^^
@@ -49,7 +59,27 @@ LL | fn baz_sugar(_: &pin const Foo) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:43:23
+  --> $DIR/feature-gate-pin_ergonomics.rs:39:31
+   |
+LL |     let mut x: Pin<&mut _> = &pin mut Foo;
+   |                               ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:44:23
+   |
+LL |     let x: Pin<&_> = &pin const Foo;
+   |                       ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:59:23
    |
 LL |         fn foo_sugar(&pin mut self) {}
    |                       ^^^
@@ -59,7 +89,7 @@ LL |         fn foo_sugar(&pin mut self) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:44:29
+  --> $DIR/feature-gate-pin_ergonomics.rs:60:29
    |
 LL |         fn foo_sugar_const(&pin const self) {}
    |                             ^^^
@@ -69,7 +99,7 @@ LL |         fn foo_sugar_const(&pin const self) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:50:18
+  --> $DIR/feature-gate-pin_ergonomics.rs:66:18
    |
 LL |         let _y: &pin mut Foo = x;
    |                  ^^^
@@ -79,7 +109,7 @@ LL |         let _y: &pin mut Foo = x;
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:53:22
+  --> $DIR/feature-gate-pin_ergonomics.rs:69:22
    |
 LL |     fn foo_sugar(_: &pin mut Foo) {}
    |                      ^^^
@@ -89,7 +119,7 @@ LL |     fn foo_sugar(_: &pin mut Foo) {}
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: pinned reference syntax is experimental
-  --> $DIR/feature-gate-pin_ergonomics.rs:65:22
+  --> $DIR/feature-gate-pin_ergonomics.rs:81:22
    |
 LL |     fn baz_sugar(_: &pin const Foo) {}
    |                      ^^^
@@ -98,8 +128,28 @@ LL |     fn baz_sugar(_: &pin const Foo) {}
    = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:84:35
+   |
+LL |         let mut x: Pin<&mut _> = &pin mut Foo;
+   |                                   ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:89:27
+   |
+LL |         let x: Pin<&_> = &pin const Foo;
+   |                           ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error[E0382]: use of moved value: `x`
-  --> $DIR/feature-gate-pin_ergonomics.rs:24:9
+  --> $DIR/feature-gate-pin_ergonomics.rs:28:9
    |
 LL | fn bar(x: Pin<&mut Foo>) {
    |        - move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
@@ -117,7 +167,7 @@ LL | fn foo(mut x: Pin<&mut Foo>) {
    |    in this function
 
 error[E0382]: use of moved value: `x`
-  --> $DIR/feature-gate-pin_ergonomics.rs:29:5
+  --> $DIR/feature-gate-pin_ergonomics.rs:33:5
    |
 LL | fn baz(mut x: Pin<&mut Foo>) {
    |        ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
@@ -136,7 +186,7 @@ help: consider reborrowing the `Pin` instead of moving it
 LL |     x.as_mut().foo();
    |      +++++++++
 
-error: aborting due to 12 previous errors
+error: aborting due to 17 previous errors
 
 Some errors have detailed explanations: E0382, E0658.
 For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr
index 4abf7dd134e..df6cada6b06 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.stderr
+++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr
@@ -90,18 +90,6 @@ LL | #[automatically_derived]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:79:1
-   |
-LL | #[track_caller]
-   | ^^^^^^^^^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:78:1
-   |
-LL | #[track_caller]
-   | ^^^^^^^^^^^^^^^
-
-error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:92:1
    |
 LL | #[export_name = "exported_symbol_name"]
@@ -278,6 +266,18 @@ LL | #[cold]
    | ^^^^^^^
 
 error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:79:1
+   |
+LL | #[track_caller]
+   | ^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:78:1
+   |
+LL | #[track_caller]
+   | ^^^^^^^^^^^^^^^
+
+error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:98:1
    |
 LL | #[no_mangle]
diff --git a/tests/ui/loop-match/break-to-block.rs b/tests/ui/loop-match/break-to-block.rs
new file mode 100644
index 00000000000..e7451a944c3
--- /dev/null
+++ b/tests/ui/loop-match/break-to-block.rs
@@ -0,0 +1,23 @@
+// Test that a `break` without `#[const_continue]` still works as expected.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+fn main() {
+    assert_eq!(helper(), 1);
+}
+
+fn helper() -> u8 {
+    let mut state = 0u8;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                0 => break 'blk 1,
+                _ => break 'a state,
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-block.rs b/tests/ui/loop-match/const-continue-to-block.rs
new file mode 100644
index 00000000000..fd7ebeefeb6
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-block.rs
@@ -0,0 +1,26 @@
+// Test that a `#[const_continue]` that breaks to a normal labeled block (that
+// is not part of a `#[loop_match]`) produces an error.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+fn const_continue_to_block() -> u8 {
+    let state = 0;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                0 => {
+                    #[const_continue]
+                    break 'blk 1;
+                }
+                _ => 'b: {
+                    #[const_continue]
+                    break 'b 2;
+                    //~^ ERROR `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-block.stderr b/tests/ui/loop-match/const-continue-to-block.stderr
new file mode 100644
index 00000000000..3a5339a0394
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-block.stderr
@@ -0,0 +1,8 @@
+error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+  --> $DIR/const-continue-to-block.rs:20:27
+   |
+LL |                     break 'b 2;
+   |                           ^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/const-continue-to-loop.rs b/tests/ui/loop-match/const-continue-to-loop.rs
new file mode 100644
index 00000000000..c363e617cfb
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-loop.rs
@@ -0,0 +1,27 @@
+// Test that a `#[const_continue]` that breaks to the label of the loop itself
+// rather than to the label of the block within the `#[loop_match]` produces an
+// error.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+fn const_continue_to_loop() -> u8 {
+    let mut state = 0;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                0 => {
+                    #[const_continue]
+                    break 'blk 1;
+                }
+                _ => {
+                    #[const_continue]
+                    break 'a 2;
+                    //~^ ERROR `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-loop.stderr b/tests/ui/loop-match/const-continue-to-loop.stderr
new file mode 100644
index 00000000000..a217b3ac72c
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-loop.stderr
@@ -0,0 +1,8 @@
+error: `#[const_continue]` must break to a labeled block that participates in a `#[loop_match]`
+  --> $DIR/const-continue-to-loop.rs:21:27
+   |
+LL |                     break 'a 2;
+   |                           ^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/const-continue-to-polymorphic-const.rs b/tests/ui/loop-match/const-continue-to-polymorphic-const.rs
new file mode 100644
index 00000000000..9a91c977911
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-polymorphic-const.rs
@@ -0,0 +1,29 @@
+// Test that a `#[const_continue]` that breaks on a polymorphic constant produces an error.
+// A polymorphic constant does not have a concrete value at MIR building time, and therefore the
+// `#[loop_match]~ desugaring can't handle such values.
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+trait Foo {
+    const TARGET: u8;
+
+    fn test_u8(mut state: u8) -> &'static str {
+        #[loop_match]
+        loop {
+            state = 'blk: {
+                match state {
+                    0 => {
+                        #[const_continue]
+                        break 'blk Self::TARGET;
+                        //~^ ERROR could not determine the target branch for this `#[const_continue]`
+                    }
+
+                    1 => return "bar",
+                    2 => return "baz",
+                    _ => unreachable!(),
+                }
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/const-continue-to-polymorphic-const.stderr b/tests/ui/loop-match/const-continue-to-polymorphic-const.stderr
new file mode 100644
index 00000000000..4d183a2fbeb
--- /dev/null
+++ b/tests/ui/loop-match/const-continue-to-polymorphic-const.stderr
@@ -0,0 +1,8 @@
+error: could not determine the target branch for this `#[const_continue]`
+  --> $DIR/const-continue-to-polymorphic-const.rs:18:36
+   |
+LL |                         break 'blk Self::TARGET;
+   |                                    ^^^^^^^^^^^^ this value is too generic
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/drop-in-match-arm.rs b/tests/ui/loop-match/drop-in-match-arm.rs
new file mode 100644
index 00000000000..731af659012
--- /dev/null
+++ b/tests/ui/loop-match/drop-in-match-arm.rs
@@ -0,0 +1,47 @@
+// Test that dropping values works in match arms, which is nontrivial
+// because each match arm needs its own scope.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+use std::sync::atomic::{AtomicBool, Ordering};
+
+fn main() {
+    assert_eq!(helper(), 1);
+    assert!(DROPPED.load(Ordering::Relaxed));
+}
+
+static DROPPED: AtomicBool = AtomicBool::new(false);
+
+struct X;
+
+impl Drop for X {
+    fn drop(&mut self) {
+        DROPPED.store(true, Ordering::Relaxed);
+    }
+}
+
+#[no_mangle]
+#[inline(never)]
+fn helper() -> i32 {
+    let mut state = 0;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                0 => match X {
+                    _ => {
+                        assert!(!DROPPED.load(Ordering::Relaxed));
+                        break 'blk 1;
+                    }
+                },
+                _ => {
+                    assert!(DROPPED.load(Ordering::Relaxed));
+                    break 'a state;
+                }
+            }
+        };
+    }
+}
diff --git a/tests/ui/loop-match/invalid-attribute.rs b/tests/ui/loop-match/invalid-attribute.rs
new file mode 100644
index 00000000000..d8d2f605eb4
--- /dev/null
+++ b/tests/ui/loop-match/invalid-attribute.rs
@@ -0,0 +1,43 @@
+// Test that the `#[loop_match]` and `#[const_continue]` attributes can only be
+// placed on expressions.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![loop_match] //~ ERROR should be applied to a loop
+#![const_continue] //~ ERROR should be applied to a break expression
+
+extern "C" {
+    #[loop_match] //~ ERROR should be applied to a loop
+    #[const_continue] //~ ERROR should be applied to a break expression
+    fn f();
+}
+
+#[loop_match] //~ ERROR should be applied to a loop
+#[const_continue] //~ ERROR should be applied to a break expression
+#[repr(C)]
+struct S {
+    a: u32,
+    b: u32,
+}
+
+trait Invoke {
+    #[loop_match] //~ ERROR should be applied to a loop
+    #[const_continue] //~ ERROR should be applied to a break expression
+    extern "C" fn invoke(&self);
+}
+
+#[loop_match] //~ ERROR should be applied to a loop
+#[const_continue] //~ ERROR should be applied to a break expression
+extern "C" fn ok() {}
+
+fn main() {
+    #[loop_match] //~ ERROR should be applied to a loop
+    #[const_continue] //~ ERROR should be applied to a break expression
+    || {};
+
+    {
+        #[loop_match] //~ ERROR should be applied to a loop
+        #[const_continue] //~ ERROR should be applied to a break expression
+        5
+    };
+}
diff --git a/tests/ui/loop-match/invalid-attribute.stderr b/tests/ui/loop-match/invalid-attribute.stderr
new file mode 100644
index 00000000000..07015311f9c
--- /dev/null
+++ b/tests/ui/loop-match/invalid-attribute.stderr
@@ -0,0 +1,131 @@
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:16:1
+   |
+LL | #[const_continue]
+   | ^^^^^^^^^^^^^^^^^
+LL | #[repr(C)]
+LL | struct S {
+   | -------- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:15:1
+   |
+LL | #[loop_match]
+   | ^^^^^^^^^^^^^
+...
+LL | struct S {
+   | -------- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:30:1
+   |
+LL | #[const_continue]
+   | ^^^^^^^^^^^^^^^^^
+LL | extern "C" fn ok() {}
+   | ------------------ not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:29:1
+   |
+LL | #[loop_match]
+   | ^^^^^^^^^^^^^
+LL | #[const_continue]
+LL | extern "C" fn ok() {}
+   | ------------------ not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:35:5
+   |
+LL |     #[const_continue]
+   |     ^^^^^^^^^^^^^^^^^
+LL |     || {};
+   |     -- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:34:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+LL |     #[const_continue]
+LL |     || {};
+   |     -- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:40:9
+   |
+LL |         #[const_continue]
+   |         ^^^^^^^^^^^^^^^^^
+LL |         5
+   |         - not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:39:9
+   |
+LL |         #[loop_match]
+   |         ^^^^^^^^^^^^^
+LL |         #[const_continue]
+LL |         5
+   |         - not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:25:5
+   |
+LL |     #[const_continue]
+   |     ^^^^^^^^^^^^^^^^^
+LL |     extern "C" fn invoke(&self);
+   |     ---------------------------- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:24:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+LL |     #[const_continue]
+LL |     extern "C" fn invoke(&self);
+   |     ---------------------------- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:11:5
+   |
+LL |     #[const_continue]
+   |     ^^^^^^^^^^^^^^^^^
+LL |     fn f();
+   |     ------- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:10:5
+   |
+LL |     #[loop_match]
+   |     ^^^^^^^^^^^^^
+LL |     #[const_continue]
+LL |     fn f();
+   |     ------- not a loop
+
+error: `#[const_continue]` should be applied to a break expression
+  --> $DIR/invalid-attribute.rs:7:1
+   |
+LL | / #![allow(incomplete_features)]
+LL | | #![feature(loop_match)]
+LL | | #![loop_match]
+LL | | #![const_continue]
+   | | ^^^^^^^^^^^^^^^^^^
+...  |
+LL | |     };
+LL | | }
+   | |_- not a break expression
+
+error: `#[loop_match]` should be applied to a loop
+  --> $DIR/invalid-attribute.rs:6:1
+   |
+LL | / #![allow(incomplete_features)]
+LL | | #![feature(loop_match)]
+LL | | #![loop_match]
+   | | ^^^^^^^^^^^^^^
+LL | | #![const_continue]
+...  |
+LL | |     };
+LL | | }
+   | |_- not a loop
+
+error: aborting due to 14 previous errors
+
diff --git a/tests/ui/loop-match/invalid.rs b/tests/ui/loop-match/invalid.rs
new file mode 100644
index 00000000000..2ddc19f4fc6
--- /dev/null
+++ b/tests/ui/loop-match/invalid.rs
@@ -0,0 +1,161 @@
+// Test that the correct error is emitted when `#[loop_match]` is applied to
+// syntax it does not support.
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn invalid_update() {
+    let mut fake = State::A;
+    let state = State::A;
+    #[loop_match]
+    loop {
+        fake = 'blk: {
+            //~^ ERROR invalid update of the `#[loop_match]` state
+            match state {
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn invalid_scrutinee() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match State::A {
+                //~^ ERROR invalid match on `#[loop_match]` state
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn bad_statements_1() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        1;
+        //~^ ERROR statements are not allowed in this position within a `#[loop_match]`
+        state = 'blk: {
+            match State::A {
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn bad_statements_2() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            1;
+            //~^ ERROR statements are not allowed in this position within a `#[loop_match]`
+            match State::A {
+                _ => State::B,
+            }
+        }
+    }
+}
+
+fn bad_rhs_1() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = State::B
+        //~^ ERROR this expression must be a single `match` wrapped in a labeled block
+    }
+}
+
+fn bad_rhs_2() {
+    let mut state = State::A;
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            State::B
+            //~^ ERROR this expression must be a single `match` wrapped in a labeled block
+        }
+    }
+}
+
+fn bad_rhs_3() {
+    let mut state = ();
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            //~^ ERROR this expression must be a single `match` wrapped in a labeled block
+        }
+    }
+}
+
+fn missing_assignment() {
+    #[loop_match]
+    loop {
+        () //~ ERROR  expected a single assignment expression
+    }
+}
+
+fn empty_loop_body() {
+    #[loop_match]
+    loop {
+        //~^ ERROR  expected a single assignment expression
+    }
+}
+
+fn break_without_value() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk;
+                    //~^ ERROR mismatched types
+                }
+                _ => break 'a,
+            }
+        }
+    }
+}
+
+fn break_without_value_unit() {
+    let mut state = ();
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                () => {
+                    #[const_continue]
+                    break 'blk;
+                    //~^ ERROR a `#[const_continue]` must break to a label with a value
+                }
+            }
+        }
+    }
+}
+
+fn arm_has_guard(cond: bool) {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B if cond => break 'a,
+                //~^ ERROR match arms that are part of a `#[loop_match]` cannot have guards
+                _ => break 'a,
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/invalid.stderr b/tests/ui/loop-match/invalid.stderr
new file mode 100644
index 00000000000..51fdd024c6f
--- /dev/null
+++ b/tests/ui/loop-match/invalid.stderr
@@ -0,0 +1,91 @@
+error[E0308]: mismatched types
+  --> $DIR/invalid.rs:120:21
+   |
+LL |                     break 'blk;
+   |                     ^^^^^^^^^^ expected `State`, found `()`
+   |
+help: give the `break` a value of the expected type
+   |
+LL |                     break 'blk /* value */;
+   |                                +++++++++++
+
+error: invalid update of the `#[loop_match]` state
+  --> $DIR/invalid.rs:18:9
+   |
+LL |         fake = 'blk: {
+   |         ^^^^
+LL |
+LL |             match state {
+   |                   ----- the assignment must update this variable
+
+error: invalid match on `#[loop_match]` state
+  --> $DIR/invalid.rs:32:19
+   |
+LL |             match State::A {
+   |                   ^^^^^^^^
+   |
+   = note: a local variable must be the scrutinee within a `#[loop_match]`
+
+error: statements are not allowed in this position within a `#[loop_match]`
+  --> $DIR/invalid.rs:44:9
+   |
+LL |         1;
+   |         ^^
+
+error: statements are not allowed in this position within a `#[loop_match]`
+  --> $DIR/invalid.rs:59:13
+   |
+LL |             1;
+   |             ^^
+
+error: this expression must be a single `match` wrapped in a labeled block
+  --> $DIR/invalid.rs:72:17
+   |
+LL |         state = State::B
+   |                 ^^^^^^^^
+
+error: this expression must be a single `match` wrapped in a labeled block
+  --> $DIR/invalid.rs:82:13
+   |
+LL |             State::B
+   |             ^^^^^^^^
+
+error: this expression must be a single `match` wrapped in a labeled block
+  --> $DIR/invalid.rs:92:17
+   |
+LL |           state = 'blk: {
+   |  _________________^
+LL | |
+LL | |         }
+   | |_________^
+
+error: expected a single assignment expression
+  --> $DIR/invalid.rs:101:9
+   |
+LL |         ()
+   |         ^^
+
+error: expected a single assignment expression
+  --> $DIR/invalid.rs:107:10
+   |
+LL |       loop {
+   |  __________^
+LL | |
+LL | |     }
+   | |_____^
+
+error: a `#[const_continue]` must break to a label with a value
+  --> $DIR/invalid.rs:137:21
+   |
+LL |                     break 'blk;
+   |                     ^^^^^^^^^^
+
+error: match arms that are part of a `#[loop_match]` cannot have guards
+  --> $DIR/invalid.rs:155:29
+   |
+LL |                 State::B if cond => break 'a,
+   |                             ^^^^
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/loop-match/loop-match.rs b/tests/ui/loop-match/loop-match.rs
new file mode 100644
index 00000000000..f38bc01f333
--- /dev/null
+++ b/tests/ui/loop-match/loop-match.rs
@@ -0,0 +1,45 @@
+// Test that a basic correct example of `#[loop_match]` with `#[const_continue]`
+// works correctly.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B => {
+                    // Without special logic, the compiler believes this is a
+                    // reassignment to an immutable variable because of the
+                    // `loop`. So this tests that local variables work.
+                    let _a = 0;
+
+                    if true {
+                        #[const_continue]
+                        break 'blk State::C;
+                    } else {
+                        #[const_continue]
+                        break 'blk State::A;
+                    }
+                }
+                State::C => break 'a,
+            }
+        };
+    }
+
+    assert!(matches!(state, State::C))
+}
diff --git a/tests/ui/loop-match/macro.rs b/tests/ui/loop-match/macro.rs
new file mode 100644
index 00000000000..98c98b9b627
--- /dev/null
+++ b/tests/ui/loop-match/macro.rs
@@ -0,0 +1,48 @@
+// Test that macros can be defined in the labeled block. This should not trigger an error about
+// statements not being allowed in that position, and should of course work as expected.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State {
+    A,
+    B,
+    C,
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            macro_rules! const_continue {
+                ($e:expr) => {
+                    #[const_continue]
+                    break 'blk $e;
+                };
+            }
+            match state {
+                State::A => {
+                    const_continue!(State::B);
+                }
+                State::B => {
+                    // Without special logic, the compiler believes this is a
+                    // reassignment to an immutable variable because of the
+                    // `loop`. So this tests that local variables work.
+                    let _a = 0;
+
+                    if true {
+                        const_continue!(State::C);
+                    } else {
+                        const_continue!(State::A);
+                    }
+                }
+                State::C => break 'a,
+            }
+        };
+    }
+
+    assert!(matches!(state, State::C))
+}
diff --git a/tests/ui/loop-match/nested.rs b/tests/ui/loop-match/nested.rs
new file mode 100644
index 00000000000..aaddfae11de
--- /dev/null
+++ b/tests/ui/loop-match/nested.rs
@@ -0,0 +1,83 @@
+// Test that a nested `#[loop_match]` works as expected, and that e.g. a
+// `#[const_continue]` of the inner `#[loop_match]` does not interact with the
+// outer `#[loop_match]`.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State1 {
+    A,
+    B,
+    C,
+}
+
+enum State2 {
+    X,
+    Y,
+    Z,
+}
+
+fn main() {
+    assert_eq!(run(), concat!("ab", "xyz", "xyz", "c"))
+}
+
+fn run() -> String {
+    let mut accum = String::new();
+
+    let mut state1 = State1::A;
+    let mut state2 = State2::X;
+
+    let mut first = true;
+
+    #[loop_match]
+    'a: loop {
+        state1 = 'blk1: {
+            match state1 {
+                State1::A => {
+                    accum.push('a');
+                    #[const_continue]
+                    break 'blk1 State1::B;
+                }
+                State1::B => {
+                    accum.push('b');
+                    #[loop_match]
+                    loop {
+                        state2 = 'blk2: {
+                            match state2 {
+                                State2::X => {
+                                    accum.push('x');
+                                    #[const_continue]
+                                    break 'blk2 State2::Y;
+                                }
+                                State2::Y => {
+                                    accum.push('y');
+                                    #[const_continue]
+                                    break 'blk2 State2::Z;
+                                }
+                                State2::Z => {
+                                    accum.push('z');
+                                    if first {
+                                        first = false;
+                                        #[const_continue]
+                                        break 'blk2 State2::X;
+                                    } else {
+                                        #[const_continue]
+                                        break 'blk1 State1::C;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                State1::C => {
+                    accum.push('c');
+                    break 'a;
+                }
+            }
+        }
+    }
+
+    accum
+}
diff --git a/tests/ui/loop-match/or-patterns.rs b/tests/ui/loop-match/or-patterns.rs
new file mode 100644
index 00000000000..775243b9c62
--- /dev/null
+++ b/tests/ui/loop-match/or-patterns.rs
@@ -0,0 +1,54 @@
+// Test that `#[loop_match]` supports or-patterns.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum State {
+    A,
+    B,
+    C,
+    D,
+}
+
+fn main() {
+    let mut states = vec![];
+    let mut first = true;
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    states.push(state);
+                    if first {
+                        #[const_continue]
+                        break 'blk State::B;
+                    } else {
+                        #[const_continue]
+                        break 'blk State::D;
+                    }
+                }
+                State::B | State::D => {
+                    states.push(state);
+                    if first {
+                        first = false;
+                        #[const_continue]
+                        break 'blk State::A;
+                    } else {
+                        #[const_continue]
+                        break 'blk State::C;
+                    }
+                }
+                State::C => {
+                    states.push(state);
+                    break 'a;
+                }
+            }
+        }
+    }
+
+    assert_eq!(states, [State::A, State::B, State::A, State::D, State::C]);
+}
diff --git a/tests/ui/loop-match/unsupported-type.rs b/tests/ui/loop-match/unsupported-type.rs
new file mode 100644
index 00000000000..9100a1103ab
--- /dev/null
+++ b/tests/ui/loop-match/unsupported-type.rs
@@ -0,0 +1,27 @@
+// Test that the right error is emitted when the `#[loop_match]` state is an
+// unsupported type.
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+#![crate_type = "lib"]
+
+fn unsupported_type() {
+    let mut state = Some(false);
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            //~^ ERROR this `#[loop_match]` state value has type `Option<bool>`, which is not supported
+            match state {
+                Some(false) => {
+                    #[const_continue]
+                    break 'blk Some(true);
+                }
+                Some(true) => {
+                    #[const_continue]
+                    break 'blk None;
+                }
+                None => break 'a,
+            }
+        }
+    }
+}
diff --git a/tests/ui/loop-match/unsupported-type.stderr b/tests/ui/loop-match/unsupported-type.stderr
new file mode 100644
index 00000000000..ede3d86796f
--- /dev/null
+++ b/tests/ui/loop-match/unsupported-type.stderr
@@ -0,0 +1,10 @@
+error: this `#[loop_match]` state value has type `Option<bool>`, which is not supported
+  --> $DIR/unsupported-type.rs:12:9
+   |
+LL |         state = 'blk: {
+   |         ^^^^^
+   |
+   = note: only integers, floats, bool, char, and enums without fields are supported
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/loop-match/unwind.rs b/tests/ui/loop-match/unwind.rs
new file mode 100644
index 00000000000..39e2e4537b1
--- /dev/null
+++ b/tests/ui/loop-match/unwind.rs
@@ -0,0 +1,53 @@
+// Test that `#[const_continue]` correctly emits cleanup paths for drops.
+//
+// Here, we first drop `DropBomb`, causing an unwind. Then `ExitOnDrop` should
+// be dropped, causing us to exit with `0` rather than with some non-zero value
+// due to the panic, which is what causes the test to pass.
+
+//@ run-pass
+//@ needs-unwind
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+enum State {
+    A,
+    B,
+}
+
+struct ExitOnDrop;
+
+impl Drop for ExitOnDrop {
+    fn drop(&mut self) {
+        std::process::exit(0);
+    }
+}
+
+struct DropBomb;
+
+impl Drop for DropBomb {
+    fn drop(&mut self) {
+        panic!("this must unwind");
+    }
+}
+
+fn main() {
+    let mut state = State::A;
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                State::A => {
+                    let _exit = ExitOnDrop;
+                    let _bomb = DropBomb;
+
+                    #[const_continue]
+                    break 'blk State::B;
+                }
+                State::B => break 'a,
+            }
+        };
+    }
+
+    unreachable!();
+}
diff --git a/tests/ui/loop-match/valid-patterns.rs b/tests/ui/loop-match/valid-patterns.rs
new file mode 100644
index 00000000000..4e0e4798a0b
--- /dev/null
+++ b/tests/ui/loop-match/valid-patterns.rs
@@ -0,0 +1,117 @@
+// Test that signed and unsigned integer patterns work with `#[loop_match]`.
+
+//@ run-pass
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+fn main() {
+    assert_eq!(integer(0), 2);
+    assert_eq!(integer(-1), 2);
+    assert_eq!(integer(2), 2);
+
+    assert_eq!(boolean(true), false);
+    assert_eq!(boolean(false), false);
+
+    assert_eq!(character('a'), 'b');
+    assert_eq!(character('b'), 'b');
+    assert_eq!(character('c'), 'd');
+    assert_eq!(character('d'), 'd');
+
+    assert_eq!(test_f32(1.0), core::f32::consts::PI);
+    assert_eq!(test_f32(2.5), core::f32::consts::PI);
+    assert_eq!(test_f32(4.0), 4.0);
+
+    assert_eq!(test_f64(1.0), core::f64::consts::PI);
+    assert_eq!(test_f64(2.5), core::f64::consts::PI);
+    assert_eq!(test_f64(4.0), 4.0);
+}
+
+fn integer(mut state: i32) -> i32 {
+    #[loop_match]
+    'a: loop {
+        state = 'blk: {
+            match state {
+                -1 => {
+                    #[const_continue]
+                    break 'blk 2;
+                }
+                0 => {
+                    #[const_continue]
+                    break 'blk -1;
+                }
+                2 => break 'a,
+                _ => unreachable!("weird value {:?}", state),
+            }
+        }
+    }
+
+    state
+}
+
+fn boolean(mut state: bool) -> bool {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                true => {
+                    #[const_continue]
+                    break 'blk false;
+                }
+                false => return state,
+            }
+        }
+    }
+}
+
+fn character(mut state: char) -> char {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                'a' => {
+                    #[const_continue]
+                    break 'blk 'b';
+                }
+                'b' => return state,
+                'c' => {
+                    #[const_continue]
+                    break 'blk 'd';
+                }
+                _ => return state,
+            }
+        }
+    }
+}
+
+fn test_f32(mut state: f32) -> f32 {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                1.0 => {
+                    #[const_continue]
+                    break 'blk 2.5;
+                }
+                2.0..3.0 => return core::f32::consts::PI,
+                _ => return state,
+            }
+        }
+    }
+}
+
+fn test_f64(mut state: f64) -> f64 {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                1.0 => {
+                    #[const_continue]
+                    break 'blk 2.5;
+                }
+                2.0..3.0 => return core::f64::consts::PI,
+                _ => return state,
+            }
+        }
+    }
+}
diff --git a/tests/ui/no_std/simple-runs.rs b/tests/ui/no_std/simple-runs.rs
index 8931ac7ed11..af44dfec311 100644
--- a/tests/ui/no_std/simple-runs.rs
+++ b/tests/ui/no_std/simple-runs.rs
@@ -4,6 +4,7 @@
 //@ compile-flags: -Cpanic=abort
 //@ ignore-wasm different `main` convention
 
+#![feature(lang_items)]
 #![no_std]
 #![no_main]
 
@@ -35,6 +36,17 @@ fn panic_handler(_info: &PanicInfo<'_>) -> ! {
     loop {}
 }
 
+#[lang = "eh_personality"]
+extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
+
 #[no_mangle]
 extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int {
     0
diff --git a/tests/ui/panic-runtime/incompatible-type.rs b/tests/ui/panic-runtime/incompatible-type.rs
index 4cbcfec11c9..f82c23d68c2 100644
--- a/tests/ui/panic-runtime/incompatible-type.rs
+++ b/tests/ui/panic-runtime/incompatible-type.rs
@@ -21,4 +21,12 @@ pub fn test(_: DropMe) {
 }
 
 #[rustc_std_internal_symbol]
-pub unsafe extern "C" fn rust_eh_personality() {}
+pub unsafe extern "C" fn rust_eh_personality(
+    _version: i32,
+    _actions: i32,
+    _exception_class: u64,
+    _exception_object: *mut (),
+    _context: *mut (),
+) -> i32 {
+    loop {}
+}
diff --git a/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr
new file mode 100644
index 00000000000..cc438461a5d
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow-unpin.pinned.stderr
@@ -0,0 +1,238 @@
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:39:14
+   |
+LL |     let foo = Foo::default();
+   |         --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo);
+   |                          --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo);
+   |                          --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:43:14
+   |
+LL |     let foo = Foo::default();
+   |         --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo;
+   |                      --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo;
+   |                      --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:52:14
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:56:14
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_move(foo);
+   |              ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:68:14
+   |
+LL |     let foo = Foo::default();
+   |         --- binding `foo` declared here
+LL |     let x = &pin const foo; // ok
+   |             -------------- borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin const foo; // ok
+   |                        --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:76:13
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_ref(&foo);
+   |             ^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:80:13
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_ref(&foo);
+   |             ^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:99:26
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_pin_mut(&pin mut foo);
+   |                          ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: use of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:103:26
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_pin_mut(&pin mut foo);
+   |                          ^^^ value used here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:115:26
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- binding `foo` declared here
+LL |     let x = &pin const foo; // ok
+   |             -------------- borrow of `foo` occurs here
+LL |     foo_pin_mut(&pin mut foo);
+   |                          ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin const foo; // ok
+   |                        --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:123:17
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- value moved here
+LL |     foo_pin_ref(&pin const foo);
+   |                 ^^^^^^^^^^^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     foo_pin_mut(&pin mut foo); // ok
+   |                          --- you could clone this value
+
+error[E0382]: borrow of moved value: `foo`
+  --> $DIR/borrow-unpin.rs:127:17
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait
+LL |     let x = &pin mut foo; // ok
+   |                      --- value moved here
+LL |     foo_pin_ref(&pin const foo);
+   |                 ^^^^^^^^^^^^^^ value borrowed here after move
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:16:1
+   |
+LL | struct Foo(PhantomPinned);
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error: aborting due to 12 previous errors
+
+Some errors have detailed explanations: E0382, E0505.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/pin-ergonomics/borrow-unpin.rs b/tests/ui/pin-ergonomics/borrow-unpin.rs
new file mode 100644
index 00000000000..61e69bab12b
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow-unpin.rs
@@ -0,0 +1,143 @@
+//@ revisions: unpin pinned
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+// For now, in order to ensure soundness, we move the place in `&pin mut place`
+// if `place` is not `Unpin`.
+// In the next step, we borrow the place instead of moving it, after that we
+// have to makes sure `&pin mut place` and `&pin const place` cannot violate
+// the mut-xor-share rules.
+
+use std::pin::Pin;
+use std::marker::PhantomPinned;
+
+#[cfg(pinned)]
+#[derive(Default)]
+struct Foo(PhantomPinned);
+
+#[cfg(unpin)]
+#[derive(Default)]
+struct Foo;
+
+fn foo_mut(_: &mut Foo) {
+}
+
+fn foo_ref(_: &Foo) {
+}
+
+fn foo_pin_mut(_: Pin<&mut Foo>) {
+}
+
+fn foo_pin_ref(_: Pin<&Foo>) {
+}
+
+fn foo_move(_: Foo) {}
+
+fn immutable_pin_mut_then_move() {
+    let foo = Foo::default();
+    foo_pin_mut(&pin mut foo); //[unpin]~ ERROR cannot borrow `foo` as mutable, as it is not declared as mutable
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+
+    let foo = Foo::default();
+    let x = &pin mut foo; //[unpin]~ ERROR cannot borrow `foo` as mutable, as it is not declared as mutable
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+    //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed
+    foo_pin_mut(x); //
+}
+
+
+fn pin_mut_then_move() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_move(foo); //[pinned]~ ERROR use of moved value: `foo`
+    //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed
+    foo_pin_mut(x); //
+}
+
+fn pin_ref_then_move() {
+    let foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_move(foo); // ok
+
+    let foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_move(foo); //[pinned]~ ERROR cannot move out of `foo` because it is borrowed
+    //[unpin]~^ ERROR cannot move out of `foo` because it is borrowed
+    foo_pin_ref(x);
+}
+
+fn pin_mut_then_ref() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_ref(&foo); //[pinned]~ ERROR borrow of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_ref(&foo); //[pinned]~ ERROR borrow of moved value: `foo`
+    //[unpin]~^ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
+    foo_pin_mut(x);
+}
+
+fn pin_ref_then_ref() {
+    let mut foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_ref(&foo); // ok
+
+    let mut foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_ref(&foo); // ok
+    foo_pin_ref(x);
+}
+
+fn pin_mut_then_pin_mut() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_pin_mut(&pin mut foo); //[pinned]~ ERROR use of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_pin_mut(&pin mut foo); //[pinned]~ ERROR use of moved value: `foo`
+    //[unpin]~^ ERROR cannot borrow `foo` as mutable more than once at a time
+    foo_pin_mut(x);
+}
+
+fn pin_ref_then_pin_mut() {
+    let mut foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_pin_mut(&pin mut foo); // ok
+
+    let mut foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_pin_mut(&pin mut foo); //[pinned]~ ERROR cannot move out of `foo` because it is borrowed
+    //[unpin]~^ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable
+    foo_pin_ref(x);
+}
+
+fn pin_mut_then_pin_ref() {
+    let mut foo = Foo::default();
+    foo_pin_mut(&pin mut foo); // ok
+    foo_pin_ref(&pin const foo); //[pinned]~ ERROR borrow of moved value: `foo`
+
+    let mut foo = Foo::default();
+    let x = &pin mut foo; // ok
+    foo_pin_ref(&pin const foo); //[pinned]~ ERROR borrow of moved value: `foo`
+    //[unpin]~^ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
+    foo_pin_mut(x);
+}
+
+fn pin_ref_then_pin_ref() {
+    let mut foo = Foo::default();
+    foo_pin_ref(&pin const foo); // ok
+    foo_pin_ref(&pin const foo); // ok
+
+    let mut foo = Foo::default();
+    let x = &pin const foo; // ok
+    foo_pin_ref(&pin const foo); // ok
+    foo_pin_ref(x);
+}
+
+fn main() {}
diff --git a/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr
new file mode 100644
index 00000000000..bf9921343ee
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow-unpin.unpin.stderr
@@ -0,0 +1,136 @@
+error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable
+  --> $DIR/borrow-unpin.rs:38:17
+   |
+LL |     foo_pin_mut(&pin mut foo);
+   |                 ^^^^^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut foo = Foo::default();
+   |         +++
+
+error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable
+  --> $DIR/borrow-unpin.rs:42:13
+   |
+LL |     let x = &pin mut foo;
+   |             ^^^^^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut foo = Foo::default();
+   |         +++
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:43:14
+   |
+LL |     let foo = Foo::default();
+   |         --- binding `foo` declared here
+LL |     let x = &pin mut foo;
+   |             ------------ borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_mut(x); //
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:20:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo;
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:56:14
+   |
+LL |     let mut foo = Foo::default();
+   |         ------- binding `foo` declared here
+LL |     let x = &pin mut foo; // ok
+   |             ------------ borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_mut(x); //
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:20:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin mut foo; // ok
+   |                      --- you could clone this value
+
+error[E0505]: cannot move out of `foo` because it is borrowed
+  --> $DIR/borrow-unpin.rs:68:14
+   |
+LL |     let foo = Foo::default();
+   |         --- binding `foo` declared here
+LL |     let x = &pin const foo; // ok
+   |             -------------- borrow of `foo` occurs here
+LL |     foo_move(foo);
+   |              ^^^ move out of `foo` occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - borrow later used here
+   |
+note: if `Foo` implemented `Clone`, you could clone the value
+  --> $DIR/borrow-unpin.rs:20:1
+   |
+LL | struct Foo;
+   | ^^^^^^^^^^ consider implementing `Clone` for this type
+...
+LL |     let x = &pin const foo; // ok
+   |                        --- you could clone this value
+
+error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
+  --> $DIR/borrow-unpin.rs:80:13
+   |
+LL |     let x = &pin mut foo; // ok
+   |             ------------ mutable borrow occurs here
+LL |     foo_ref(&foo);
+   |             ^^^^ immutable borrow occurs here
+LL |
+LL |     foo_pin_mut(x);
+   |                 - mutable borrow later used here
+
+error[E0499]: cannot borrow `foo` as mutable more than once at a time
+  --> $DIR/borrow-unpin.rs:103:17
+   |
+LL |     let x = &pin mut foo; // ok
+   |             ------------ first mutable borrow occurs here
+LL |     foo_pin_mut(&pin mut foo);
+   |                 ^^^^^^^^^^^^ second mutable borrow occurs here
+LL |
+LL |     foo_pin_mut(x);
+   |                 - first borrow later used here
+
+error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
+  --> $DIR/borrow-unpin.rs:115:17
+   |
+LL |     let x = &pin const foo; // ok
+   |             -------------- immutable borrow occurs here
+LL |     foo_pin_mut(&pin mut foo);
+   |                 ^^^^^^^^^^^^ mutable borrow occurs here
+LL |
+LL |     foo_pin_ref(x);
+   |                 - immutable borrow later used here
+
+error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
+  --> $DIR/borrow-unpin.rs:127:17
+   |
+LL |     let x = &pin mut foo; // ok
+   |             ------------ mutable borrow occurs here
+LL |     foo_pin_ref(&pin const foo);
+   |                 ^^^^^^^^^^^^^^ immutable borrow occurs here
+LL |
+LL |     foo_pin_mut(x);
+   |                 - mutable borrow later used here
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0499, E0502, E0505, E0596.
+For more information about an error, try `rustc --explain E0499`.
diff --git a/tests/ui/pin-ergonomics/borrow.rs b/tests/ui/pin-ergonomics/borrow.rs
new file mode 100644
index 00000000000..f221165848b
--- /dev/null
+++ b/tests/ui/pin-ergonomics/borrow.rs
@@ -0,0 +1,38 @@
+//@ check-pass
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+// Makes sure we can handle `&pin mut place` and `&pin const place` as sugar for
+// `std::pin::pin!(place)` and `Pin::new(&place)`.
+
+use std::pin::Pin;
+
+struct Foo;
+
+fn foo_pin_mut(_: Pin<&mut Foo>) {
+}
+
+fn foo_pin_ref(_: Pin<&Foo>) {
+}
+
+fn bar() {
+    let mut x: Pin<&mut _> = &pin mut Foo;
+    foo_pin_mut(x.as_mut());
+    foo_pin_mut(x.as_mut());
+    foo_pin_ref(x);
+
+    let x: Pin<&_> = &pin const Foo;
+
+    foo_pin_ref(x);
+    foo_pin_ref(x);
+}
+
+fn baz(mut x: Foo, y: Foo) {
+    let _x = &pin mut x;
+    let _x = x; // ok because `Foo: Unpin` and thus `&pin mut x` doesn't move `x`
+
+    let _y = &pin const y;
+    let _y = y; // ok because `&pin const y` dosn't move `y`
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.rs b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.rs
index a95665f126d..a95665f126d 100644
--- a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.rs
+++ b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.rs
diff --git a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.stderr b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.stderr
index 2deb5b09884..2deb5b09884 100644
--- a/tests/ui/async-await/pin-ergonomics/coerce-non-pointer-pin.stderr
+++ b/tests/ui/pin-ergonomics/coerce-non-pointer-pin.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-arg.rs b/tests/ui/pin-ergonomics/reborrow-arg.rs
index 2008bd1f52d..2008bd1f52d 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-arg.rs
+++ b/tests/ui/pin-ergonomics/reborrow-arg.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.rs b/tests/ui/pin-ergonomics/reborrow-const-as-mut.rs
index 27c70a7b4df..27c70a7b4df 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.rs
+++ b/tests/ui/pin-ergonomics/reborrow-const-as-mut.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.stderr b/tests/ui/pin-ergonomics/reborrow-const-as-mut.stderr
index 36bbf1c493a..36bbf1c493a 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-const-as-mut.stderr
+++ b/tests/ui/pin-ergonomics/reborrow-const-as-mut.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-once.rs b/tests/ui/pin-ergonomics/reborrow-once.rs
index 241efadef7d..241efadef7d 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-once.rs
+++ b/tests/ui/pin-ergonomics/reborrow-once.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr b/tests/ui/pin-ergonomics/reborrow-once.stderr
index dc8e424ad2a..dc8e424ad2a 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-once.stderr
+++ b/tests/ui/pin-ergonomics/reborrow-once.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-self.rs b/tests/ui/pin-ergonomics/reborrow-self.rs
index ee617617da0..ee617617da0 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-self.rs
+++ b/tests/ui/pin-ergonomics/reborrow-self.rs
diff --git a/tests/ui/async-await/pin-ergonomics/reborrow-shorter.rs b/tests/ui/pin-ergonomics/reborrow-shorter.rs
index 06c266e0035..06c266e0035 100644
--- a/tests/ui/async-await/pin-ergonomics/reborrow-shorter.rs
+++ b/tests/ui/pin-ergonomics/reborrow-shorter.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-ambiguity.rs b/tests/ui/pin-ergonomics/sugar-ambiguity.rs
index d183000931e..d183000931e 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-ambiguity.rs
+++ b/tests/ui/pin-ergonomics/sugar-ambiguity.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-no-const.rs b/tests/ui/pin-ergonomics/sugar-no-const.rs
index dd6456b6034..dd6456b6034 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-no-const.rs
+++ b/tests/ui/pin-ergonomics/sugar-no-const.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr b/tests/ui/pin-ergonomics/sugar-no-const.stderr
index 062b6d3f487..062b6d3f487 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr
+++ b/tests/ui/pin-ergonomics/sugar-no-const.stderr
diff --git a/tests/ui/async-await/pin-ergonomics/sugar-self.rs b/tests/ui/pin-ergonomics/sugar-self.rs
index 3d71b54b1ae..3d71b54b1ae 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar-self.rs
+++ b/tests/ui/pin-ergonomics/sugar-self.rs
diff --git a/tests/ui/async-await/pin-ergonomics/sugar.rs b/tests/ui/pin-ergonomics/sugar.rs
index 8dbdec418b1..8dbdec418b1 100644
--- a/tests/ui/async-await/pin-ergonomics/sugar.rs
+++ b/tests/ui/pin-ergonomics/sugar.rs
diff --git a/tests/ui/print-calling-conventions.stdout b/tests/ui/print-calling-conventions.stdout
index 7b5ae495660..4df6bd27f45 100644
--- a/tests/ui/print-calling-conventions.stdout
+++ b/tests/ui/print-calling-conventions.stdout
@@ -20,6 +20,7 @@ riscv-interrupt-m
 riscv-interrupt-s
 rust-call
 rust-cold
+rust-invalid
 stdcall
 stdcall-unwind
 system
diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr
index e22d812c8b0..6088945b829 100644
--- a/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr
+++ b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr
@@ -1,8 +1,12 @@
-error: malformed `track_caller` attribute input
+error[E0565]: malformed `track_caller` attribute input
   --> $DIR/error-odd-syntax.rs:1:1
    |
 LL | #[track_caller(1)]
-   | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[track_caller]`
+   | ^^^^^^^^^^^^^^---^
+   | |             |
+   | |             didn't expect any arguments here
+   | help: must be of the form: `#[track_caller]`
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0565`.
diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr
index f87e34622b9..00c6b55c6a2 100644
--- a/tests/ui/stats/macro-stats.stderr
+++ b/tests/ui/stats/macro-stats.stderr
@@ -2,25 +2,25 @@ macro-stats ====================================================================
 macro-stats MACRO EXPANSION STATS: macro_stats
 macro-stats Macro Name                         Uses      Lines  Avg Lines      Bytes  Avg Bytes
 macro-stats -----------------------------------------------------------------------------------
-macro-stats #[derive(Clone)]                      8         56        7.0      1_660      207.5
-macro-stats #[derive(PartialOrd)]                 1         16       16.0        654      654.0
-macro-stats #[derive(Hash)]                       2         15        7.5        547      273.5
-macro-stats #[derive(Ord)]                        1         14       14.0        489      489.0
-macro-stats q!                                    1         24       24.0        435      435.0
-macro-stats #[derive(Default)]                    2         14        7.0        367      183.5
-macro-stats #[derive(Eq)]                         1         10       10.0        312      312.0
-macro-stats #[derive(Debug)]                      1          7        7.0        261      261.0
-macro-stats #[derive(PartialEq)]                  1          8        8.0        247      247.0
-macro-stats #[derive(Copy)]                       1          1        1.0         46       46.0
-macro-stats p!                                    1          2        2.0         28       28.0
-macro-stats trait_impl_tys!                       1          1        1.0         11       11.0
-macro-stats foreign_item!                         1          0        0.0          6        6.0
-macro-stats impl_const!                           1          0        0.0          4        4.0
-macro-stats trait_tys!                            1          1        1.0          3        3.0
-macro-stats u32!                                  1          0        0.0         -3       -3.0
-macro-stats none!                                 1          0        0.0         -3       -3.0
-macro-stats n99!                                  2          0        0.0         -8       -4.0
+macro-stats #[derive(Clone)]                      8         64        8.0      1_788      223.5
+macro-stats #[derive(PartialOrd)]                 1         17       17.0        675      675.0
+macro-stats #[derive(Hash)]                       2         17        8.5        577      288.5
+macro-stats q!                                    1         26       26.0        519      519.0
+macro-stats #[derive(Ord)]                        1         15       15.0        503      503.0
+macro-stats #[derive(Default)]                    2         16        8.0        403      201.5
+macro-stats #[derive(Eq)]                         1         11       11.0        325      325.0
+macro-stats #[derive(Debug)]                      1          8        8.0        277      277.0
+macro-stats #[derive(PartialEq)]                  1          9        9.0        267      267.0
+macro-stats #[derive(Copy)]                       1          2        2.0         61       61.0
+macro-stats p!                                    1          3        3.0         32       32.0
+macro-stats trait_impl_tys!                       1          2        2.0         28       28.0
+macro-stats foreign_item!                         1          1        1.0         21       21.0
 macro-stats this_is_a_really_really_long_macro_name!
-macro-stats                                       1          0        0.0        -30      -30.0
-macro-stats #[test]                               1         -6       -6.0       -158     -158.0
+macro-stats                                       1          1        1.0         18       18.0
+macro-stats impl_const!                           1          1        1.0         17       17.0
+macro-stats trait_tys!                            1          2        2.0         15       15.0
+macro-stats n99!                                  2          2        1.0          4        2.0
+macro-stats none!                                 1          1        1.0          4        4.0
+macro-stats u32!                                  1          1        1.0          3        3.0
+macro-stats #[test]                               1          1        1.0          0        0.0
 macro-stats ===================================================================================
diff --git a/tests/ui/thir-print/thir-tree-loop-match.rs b/tests/ui/thir-print/thir-tree-loop-match.rs
new file mode 100644
index 00000000000..8c5f2244d54
--- /dev/null
+++ b/tests/ui/thir-print/thir-tree-loop-match.rs
@@ -0,0 +1,22 @@
+//@ check-pass
+//@ compile-flags: -Zunpretty=thir-tree
+
+#![allow(incomplete_features)]
+#![feature(loop_match)]
+
+fn boolean(mut state: bool) -> bool {
+    #[loop_match]
+    loop {
+        state = 'blk: {
+            match state {
+                true => {
+                    #[const_continue]
+                    break 'blk false;
+                }
+                false => return state,
+            }
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout
new file mode 100644
index 00000000000..828b93da6be
--- /dev/null
+++ b/tests/ui/thir-print/thir-tree-loop-match.stdout
@@ -0,0 +1,301 @@
+DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean):
+params: [
+    Param {
+        ty: bool
+        ty_span: Some($DIR/thir-tree-loop-match.rs:7:23: 7:27 (#0))
+        self_kind: None
+        hir_id: Some(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).1))
+        param: Some( 
+            Pat: {
+                ty: bool
+                span: $DIR/thir-tree-loop-match.rs:7:12: 7:21 (#0)
+                kind: PatKind {
+                    Binding {
+                        name: "state"
+                        mode: BindingMode(No, Mut)
+                        var: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2))
+                        ty: bool
+                        is_primary: true
+                        subpattern: None
+                    }
+                }
+            }
+        )
+    }
+]
+body:
+    Expr {
+        ty: bool
+        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+        span: $DIR/thir-tree-loop-match.rs:7:37: 20:2 (#0)
+        kind: 
+            Scope {
+                region_scope: Node(28)
+                lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).28))
+                value:
+                    Expr {
+                        ty: bool
+                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                        span: $DIR/thir-tree-loop-match.rs:7:37: 20:2 (#0)
+                        kind: 
+                            Block {
+                                targeted_by_break: false
+                                span: $DIR/thir-tree-loop-match.rs:7:37: 20:2 (#0)
+                                region_scope: Node(3)
+                                safety_mode: Safe
+                                stmts: []
+                                expr:
+                                    Expr {
+                                        ty: bool
+                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                                        span: $DIR/thir-tree-loop-match.rs:9:5: 19:6 (#0)
+                                        kind: 
+                                            Scope {
+                                                region_scope: Node(4)
+                                                lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).4))
+                                                value:
+                                                    Expr {
+                                                        ty: bool
+                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                                                        span: $DIR/thir-tree-loop-match.rs:9:5: 19:6 (#0)
+                                                        kind: 
+                                                            NeverToAny {
+                                                                source:
+                                                                    Expr {
+                                                                        ty: !
+                                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None }
+                                                                        span: $DIR/thir-tree-loop-match.rs:9:5: 19:6 (#0)
+                                                                        kind: 
+                                                                            LoopMatch {
+                                                                                state:
+                                                                                    Expr {
+                                                                                        ty: bool
+                                                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(5)), backwards_incompatible: None }
+                                                                                        span: $DIR/thir-tree-loop-match.rs:10:9: 10:14 (#0)
+                                                                                        kind: 
+                                                                                            Scope {
+                                                                                                region_scope: Node(7)
+                                                                                                lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).7))
+                                                                                                value:
+                                                                                                    Expr {
+                                                                                                        ty: bool
+                                                                                                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(5)), backwards_incompatible: None }
+                                                                                                        span: $DIR/thir-tree-loop-match.rs:10:9: 10:14 (#0)
+                                                                                                        kind: 
+                                                                                                            VarRef {
+                                                                                                                id: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2))
+                                                                                                            }
+                                                                                                    }
+                                                                                            }
+                                                                                    }
+                                                                                region_scope: Node(10)
+                                                                                match_span: $DIR/thir-tree-loop-match.rs:11:13: 17:14 (#0)
+                                                                                arms: [
+                                                                                    Arm {
+                                                                                        pattern: 
+                                                                                            Pat: {
+                                                                                                ty: bool
+                                                                                                span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0)
+                                                                                                kind: PatKind {
+                                                                                                    Constant {
+                                                                                                        value: Ty(bool, true)
+                                                                                                    }
+                                                                                                }
+                                                                                            }
+                                                                                        guard: None
+                                                                                        body: 
+                                                                                            Expr {
+                                                                                                ty: bool
+                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None }
+                                                                                                span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                kind: 
+                                                                                                    Scope {
+                                                                                                        region_scope: Node(17)
+                                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).17))
+                                                                                                        value:
+                                                                                                            Expr {
+                                                                                                                ty: bool
+                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None }
+                                                                                                                span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                                kind: 
+                                                                                                                    NeverToAny {
+                                                                                                                        source:
+                                                                                                                            Expr {
+                                                                                                                                ty: !
+                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None }
+                                                                                                                                span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                                                kind: 
+                                                                                                                                    Block {
+                                                                                                                                        targeted_by_break: false
+                                                                                                                                        span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0)
+                                                                                                                                        region_scope: Node(18)
+                                                                                                                                        safety_mode: Safe
+                                                                                                                                        stmts: [
+                                                                                                                                            Stmt {
+                                                                                                                                                kind: Expr {
+                                                                                                                                                    scope: Node(21)
+                                                                                                                                                    expr:
+                                                                                                                                                        Expr {
+                                                                                                                                                            ty: !
+                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0)
+                                                                                                                                                            kind: 
+                                                                                                                                                                Scope {
+                                                                                                                                                                    region_scope: Node(19)
+                                                                                                                                                                    lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).19))
+                                                                                                                                                                    value:
+                                                                                                                                                                        Expr {
+                                                                                                                                                                            ty: !
+                                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0)
+                                                                                                                                                                            kind: 
+                                                                                                                                                                                ConstContinue (
+                                                                                                                                                                                    label: Node(10)
+                                                                                                                                                                                    value:
+                                                                                                                                                                                        Expr {
+                                                                                                                                                                                            ty: bool
+                                                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0)
+                                                                                                                                                                                            kind: 
+                                                                                                                                                                                                Scope {
+                                                                                                                                                                                                    region_scope: Node(20)
+                                                                                                                                                                                                    lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).20))
+                                                                                                                                                                                                    value:
+                                                                                                                                                                                                        Expr {
+                                                                                                                                                                                                            ty: bool
+                                                                                                                                                                                                            temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None }
+                                                                                                                                                                                                            span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0)
+                                                                                                                                                                                                            kind: 
+                                                                                                                                                                                                                Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) }, neg: false)
+
+                                                                                                                                                                                                        }
+                                                                                                                                                                                                }
+                                                                                                                                                                                        }
+                                                                                                                                                                                )
+                                                                                                                                                                        }
+                                                                                                                                                                }
+                                                                                                                                                        }
+                                                                                                                                                }
+                                                                                                                                            }
+                                                                                                                                        ]
+                                                                                                                                        expr: []
+                                                                                                                                    }
+                                                                                                                            }
+                                                                                                                    }
+                                                                                                            }
+                                                                                                    }
+                                                                                            }
+                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).16))
+                                                                                        scope: Node(16)
+                                                                                        span: $DIR/thir-tree-loop-match.rs:12:17: 15:18 (#0)
+                                                                                    }
+                                                                                    Arm {
+                                                                                        pattern: 
+                                                                                            Pat: {
+                                                                                                ty: bool
+                                                                                                span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0)
+                                                                                                kind: PatKind {
+                                                                                                    Constant {
+                                                                                                        value: Ty(bool, false)
+                                                                                                    }
+                                                                                                }
+                                                                                            }
+                                                                                        guard: None
+                                                                                        body: 
+                                                                                            Expr {
+                                                                                                ty: bool
+                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0)
+                                                                                                kind: 
+                                                                                                    Scope {
+                                                                                                        region_scope: Node(25)
+                                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).25))
+                                                                                                        value:
+                                                                                                            Expr {
+                                                                                                                ty: bool
+                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0)
+                                                                                                                kind: 
+                                                                                                                    NeverToAny {
+                                                                                                                        source:
+                                                                                                                            Expr {
+                                                                                                                                ty: !
+                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0)
+                                                                                                                                kind: 
+                                                                                                                                    Return {
+                                                                                                                                        value:
+                                                                                                                                            Expr {
+                                                                                                                                                ty: bool
+                                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0)
+                                                                                                                                                kind: 
+                                                                                                                                                    Scope {
+                                                                                                                                                        region_scope: Node(26)
+                                                                                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).26))
+                                                                                                                                                        value:
+                                                                                                                                                            Expr {
+                                                                                                                                                                ty: bool
+                                                                                                                                                                temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None }
+                                                                                                                                                                span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0)
+                                                                                                                                                                kind: 
+                                                                                                                                                                    VarRef {
+                                                                                                                                                                        id: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2))
+                                                                                                                                                                    }
+                                                                                                                                                            }
+                                                                                                                                                    }
+                                                                                                                                            }
+                                                                                                                                    }
+                                                                                                                            }
+                                                                                                                    }
+                                                                                                            }
+                                                                                                    }
+                                                                                            }
+                                                                                        lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).24))
+                                                                                        scope: Node(24)
+                                                                                        span: $DIR/thir-tree-loop-match.rs:16:17: 16:38 (#0)
+                                                                                    }
+                                                                                ]
+                                                                            }
+                                                                    }
+                                                            }
+                                                    }
+                                            }
+                                    }
+                            }
+                    }
+            }
+    }
+
+
+DefId(0:4 ~ thir_tree_loop_match[3c53]::main):
+params: [
+]
+body:
+    Expr {
+        ty: ()
+        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None }
+        span: $DIR/thir-tree-loop-match.rs:22:11: 22:13 (#0)
+        kind: 
+            Scope {
+                region_scope: Node(2)
+                lint_level: Explicit(HirId(DefId(0:4 ~ thir_tree_loop_match[3c53]::main).2))
+                value:
+                    Expr {
+                        ty: ()
+                        temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None }
+                        span: $DIR/thir-tree-loop-match.rs:22:11: 22:13 (#0)
+                        kind: 
+                            Block {
+                                targeted_by_break: false
+                                span: $DIR/thir-tree-loop-match.rs:22:11: 22:13 (#0)
+                                region_scope: Node(1)
+                                safety_mode: Safe
+                                stmts: []
+                                expr: []
+                            }
+                    }
+            }
+    }
+
+