about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--compiler/rustc_ast/src/ast.rs11
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs4
-rw-r--r--compiler/rustc_ast/src/visit.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/pat.rs2
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs166
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0751.md2
-rw-r--r--compiler/rustc_expand/src/expand.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_lint/src/unused.rs2
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs58
-rw-r--r--compiler/rustc_parse/src/parser/nonterminal.rs2
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs54
-rw-r--r--compiler/rustc_parse/src/parser/path.rs2
-rw-r--r--compiler/rustc_passes/src/input_stats.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/intrinsics/mod.rs66
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs2
-rw-r--r--src/tools/compiletest/src/runtest.rs136
-rw-r--r--src/tools/compiletest/src/runtest/coverage.rs16
-rw-r--r--src/tools/compiletest/src/runtest/ui.rs2
-rw-r--r--src/tools/rustfmt/src/patterns.rs6
-rw-r--r--src/tools/rustfmt/tests/target/guard_patterns.rs12
-rw-r--r--tests/ui/assign-imm-local-twice.rs13
-rw-r--r--tests/ui/assoc-lang-items.rs21
-rw-r--r--tests/ui/assoc-oddities-3.rs13
-rw-r--r--tests/ui/atomic-from-mut-not-available.rs7
-rw-r--r--tests/ui/borrowck/assign-imm-local-twice.fixed21
-rw-r--r--tests/ui/borrowck/assign-imm-local-twice.rs21
-rw-r--r--tests/ui/borrowck/assign-imm-local-twice.stderr (renamed from tests/ui/assign-imm-local-twice.stderr)4
-rw-r--r--tests/ui/codegen/assign-expr-unit-type.rs (renamed from tests/ui/assign-assign.rs)13
-rw-r--r--tests/ui/feature-gates/feature-gate-guard-patterns.rs46
-rw-r--r--tests/ui/feature-gates/feature-gate-guard-patterns.stderr119
-rw-r--r--tests/ui/lang-items/assoc-lang-items.rs35
-rw-r--r--tests/ui/lang-items/assoc-lang-items.stderr (renamed from tests/ui/assoc-lang-items.stderr)8
-rw-r--r--tests/ui/parser/assoc/assoc-oddities-3.rs41
-rw-r--r--tests/ui/parser/issues/issue-72373.rs2
-rw-r--r--tests/ui/parser/issues/issue-72373.stderr4
-rw-r--r--tests/ui/parser/misspelled-keywords/ref.stderr4
-rw-r--r--tests/ui/parser/pat-lt-bracket-7.rs2
-rw-r--r--tests/ui/parser/pat-lt-bracket-7.stderr4
-rw-r--r--tests/ui/parser/recover/recover-pat-exprs.rs6
-rw-r--r--tests/ui/parser/recover/recover-pat-exprs.stderr12
-rw-r--r--tests/ui/parser/recover/recover-pat-wildcards.rs4
-rw-r--r--tests/ui/parser/recover/recover-pat-wildcards.stderr8
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr4
-rw-r--r--tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs20
-rw-r--r--tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr13
-rw-r--r--tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr (renamed from tests/ui/atomic-from-mut-not-available.stderr)2
-rw-r--r--tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs27
52 files changed, 739 insertions, 301 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f59dcdcf28d..c1b3e5e6f42 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2815,9 +2815,9 @@ dependencies = [
 
 [[package]]
 name = "psm"
-version = "0.1.23"
+version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205"
+checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
 dependencies = [
  "cc",
 ]
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 2f55a9eaeda..69ba78282f9 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -627,9 +627,11 @@ impl Pat {
             | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
 
             // Trivial wrappers over inner patterns.
-            PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => {
-                s.walk(it)
-            }
+            PatKind::Box(s)
+            | PatKind::Deref(s)
+            | PatKind::Ref(s, _)
+            | PatKind::Paren(s)
+            | PatKind::Guard(s, _) => s.walk(it),
 
             // These patterns do not contain subpatterns, skip.
             PatKind::Wild
@@ -839,6 +841,9 @@ pub enum PatKind {
     // A never pattern `!`.
     Never,
 
+    /// A guard pattern (e.g., `x if guard(x)`).
+    Guard(P<Pat>, P<Expr>),
+
     /// Parentheses in patterns used for grouping (i.e., `(PAT)`).
     Paren(P<Pat>),
 
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 622c260868e..3a4a8ce266e 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1525,6 +1525,10 @@ pub fn walk_pat<T: MutVisitor>(vis: &mut T, pat: &mut P<Pat>) {
             visit_opt(e2, |e| vis.visit_expr(e));
             vis.visit_span(span);
         }
+        PatKind::Guard(p, e) => {
+            vis.visit_pat(p);
+            vis.visit_expr(e);
+        }
         PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
             visit_thin_vec(elems, |elem| vis.visit_pat(elem))
         }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 2f6998783fa..0b000c8cef8 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -682,6 +682,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
             visit_opt!(visitor, visit_expr, lower_bound);
             visit_opt!(visitor, visit_expr, upper_bound);
         }
+        PatKind::Guard(subpattern, guard_condition) => {
+            try_visit!(visitor.visit_pat(subpattern));
+            try_visit!(visitor.visit_expr(guard_condition));
+        }
         PatKind::Wild | PatKind::Rest | PatKind::Never => {}
         PatKind::Err(_guar) => {}
         PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => {
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index ace7bfb5c73..c4bae084a3f 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             self.lower_range_end(end, e2.is_some()),
                         );
                     }
+                    // FIXME(guard_patterns): lower pattern guards to HIR
+                    PatKind::Guard(inner, _) => pattern = inner,
                     PatKind::Slice(pats) => break self.lower_pat_slice(pats),
                     PatKind::Rest => {
                         // If we reach here the `..` pattern is not semantically allowed.
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 89311516081..390a575a186 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -556,6 +556,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
     gate_all!(explicit_tail_calls, "`become` expression is experimental");
     gate_all!(generic_const_items, "generic const items are experimental");
+    gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards");
     gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
     gate_all!(postfix_match, "postfix match is experimental");
     gate_all!(mut_ref, "mutable by-reference bindings are experimental");
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 479677b0a5a..49e4a559e73 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1709,6 +1709,14 @@ impl<'a> State<'a> {
                     self.print_expr(e, FixupContext::default());
                 }
             }
+            PatKind::Guard(subpat, condition) => {
+                self.popen();
+                self.print_pat(subpat);
+                self.space();
+                self.word_space("if");
+                self.print_expr(condition, FixupContext::default());
+                self.pclose();
+            }
             PatKind::Slice(elts) => {
                 self.word("[");
                 self.commasep(Inconsistent, elts, |s, p| s.print_pat(p));
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index d8b055137b3..c38c5d4c644 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -352,84 +352,84 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
             | sym::saturating_add
             | sym::saturating_sub => {
                 let ty = arg_tys[0];
-                match int_type_width_signed(ty, self) {
-                    Some((width, signed)) => match name {
-                        sym::ctlz | sym::cttz => {
-                            let y = self.const_bool(false);
-                            let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[
-                                args[0].immediate(),
-                                y,
-                            ]);
-
-                            self.intcast(ret, llret_ty, false)
-                        }
-                        sym::ctlz_nonzero => {
-                            let y = self.const_bool(true);
-                            let llvm_name = &format!("llvm.ctlz.i{width}");
-                            let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
-                            self.intcast(ret, llret_ty, false)
-                        }
-                        sym::cttz_nonzero => {
-                            let y = self.const_bool(true);
-                            let llvm_name = &format!("llvm.cttz.i{width}");
-                            let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
-                            self.intcast(ret, llret_ty, false)
-                        }
-                        sym::ctpop => {
-                            let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[args
-                                [0]
-                            .immediate()]);
-                            self.intcast(ret, llret_ty, false)
-                        }
-                        sym::bswap => {
-                            if width == 8 {
-                                args[0].immediate() // byte swap a u8/i8 is just a no-op
-                            } else {
-                                self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[
-                                    args[0].immediate()
-                                ])
-                            }
-                        }
-                        sym::bitreverse => self
-                            .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[
+                if !ty.is_integral() {
+                    tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
+                        span,
+                        name,
+                        ty,
+                    });
+                    return Ok(());
+                }
+                let (size, signed) = ty.int_size_and_signed(self.tcx);
+                let width = size.bits();
+                match name {
+                    sym::ctlz | sym::cttz => {
+                        let y = self.const_bool(false);
+                        let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[
+                            args[0].immediate(),
+                            y,
+                        ]);
+
+                        self.intcast(ret, llret_ty, false)
+                    }
+                    sym::ctlz_nonzero => {
+                        let y = self.const_bool(true);
+                        let llvm_name = &format!("llvm.ctlz.i{width}");
+                        let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
+                        self.intcast(ret, llret_ty, false)
+                    }
+                    sym::cttz_nonzero => {
+                        let y = self.const_bool(true);
+                        let llvm_name = &format!("llvm.cttz.i{width}");
+                        let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
+                        self.intcast(ret, llret_ty, false)
+                    }
+                    sym::ctpop => {
+                        let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[
+                            args[0].immediate()
+                        ]);
+                        self.intcast(ret, llret_ty, false)
+                    }
+                    sym::bswap => {
+                        if width == 8 {
+                            args[0].immediate() // byte swap a u8/i8 is just a no-op
+                        } else {
+                            self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[
                                 args[0].immediate()
-                            ]),
-                        sym::rotate_left | sym::rotate_right => {
-                            let is_left = name == sym::rotate_left;
-                            let val = args[0].immediate();
-                            let raw_shift = args[1].immediate();
-                            // rotate = funnel shift with first two args the same
-                            let llvm_name =
-                                &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
-
-                            // llvm expects shift to be the same type as the values, but rust
-                            // always uses `u32`.
-                            let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
-
-                            self.call_intrinsic(llvm_name, &[val, val, raw_shift])
+                            ])
                         }
-                        sym::saturating_add | sym::saturating_sub => {
-                            let is_add = name == sym::saturating_add;
-                            let lhs = args[0].immediate();
-                            let rhs = args[1].immediate();
-                            let llvm_name = &format!(
-                                "llvm.{}{}.sat.i{}",
-                                if signed { 's' } else { 'u' },
-                                if is_add { "add" } else { "sub" },
-                                width
-                            );
-                            self.call_intrinsic(llvm_name, &[lhs, rhs])
-                        }
-                        _ => bug!(),
-                    },
-                    None => {
-                        tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
-                            span,
-                            name,
-                            ty,
-                        });
-                        return Ok(());
                     }
+                    sym::bitreverse => self
+                        .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[
+                            args[0].immediate()
+                        ]),
+                    sym::rotate_left | sym::rotate_right => {
+                        let is_left = name == sym::rotate_left;
+                        let val = args[0].immediate();
+                        let raw_shift = args[1].immediate();
+                        // rotate = funnel shift with first two args the same
+                        let llvm_name =
+                            &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
+
+                        // llvm expects shift to be the same type as the values, but rust
+                        // always uses `u32`.
+                        let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
+
+                        self.call_intrinsic(llvm_name, &[val, val, raw_shift])
+                    }
+                    sym::saturating_add | sym::saturating_sub => {
+                        let is_add = name == sym::saturating_add;
+                        let lhs = args[0].immediate();
+                        let rhs = args[1].immediate();
+                        let llvm_name = &format!(
+                            "llvm.{}{}.sat.i{}",
+                            if signed { 's' } else { 'u' },
+                            if is_add { "add" } else { "sub" },
+                            width
+                        );
+                        self.call_intrinsic(llvm_name, &[lhs, rhs])
+                    }
+                    _ => bug!(),
                 }
             }
 
@@ -2531,19 +2531,3 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
 
     span_bug!(span, "unknown SIMD intrinsic");
 }
-
-// Returns the width of an int Ty, and if it's signed or not
-// Returns None if the type is not an integer
-// FIXME: there’s multiple of this functions, investigate using some of the already existing
-// stuffs.
-fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> {
-    match ty.kind() {
-        ty::Int(t) => {
-            Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true))
-        }
-        ty::Uint(t) => {
-            Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false))
-        }
-        _ => None,
-    }
-}
diff --git a/compiler/rustc_error_codes/src/error_codes/E0751.md b/compiler/rustc_error_codes/src/error_codes/E0751.md
index 8794f7868f3..825809b229a 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0751.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0751.md
@@ -9,4 +9,4 @@ impl !MyTrait for i32 { } // error!
 ```
 
 Negative implementations are a promise that the trait will never be implemented
-for the given types. Therefore, both cannot exists at the same time.
+for the given types. Therefore, both cannot exist at the same time.
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index e5500c8bba1..6a6496f9827 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>(
             }
         }
         AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?),
-        AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt(
+        AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard(
             None,
             RecoverComma::No,
             RecoverColon::Yes,
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index abc7200699c..bf26b5d25d2 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -505,6 +505,8 @@ declare_features! (
     (incomplete, generic_const_items, "1.73.0", Some(113521)),
     /// Allows registering static items globally, possibly across crates, to iterate over at runtime.
     (unstable, global_registration, "1.80.0", Some(125119)),
+    /// Allows using guards in patterns.
+    (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)),
     /// Allows using `..=X` as a patterns in slices.
     (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
     /// Allows `if let` guard in match arms.
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index b775cd37409..9cad5d98562 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens {
                 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
             },
             // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
-            Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
+            Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
             // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
             // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
             Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 8d16d44b0a2..3a9e9b480ec 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2631,7 +2631,7 @@ impl<'a> Parser<'a> {
         };
         self.bump(); // Eat `let` token
         let lo = self.prev_token.span;
-        let pat = self.parse_pat_allow_top_alt(
+        let pat = self.parse_pat_no_top_guard(
             None,
             RecoverComma::Yes,
             RecoverColon::Yes,
@@ -2778,7 +2778,7 @@ impl<'a> Parser<'a> {
         };
         // Try to parse the pattern `for ($PAT) in $EXPR`.
         let pat = match (
-            self.parse_pat_allow_top_alt(
+            self.parse_pat_allow_top_guard(
                 None,
                 RecoverComma::Yes,
                 RecoverColon::Yes,
@@ -3241,7 +3241,7 @@ impl<'a> Parser<'a> {
                     // then we should recover.
                     let mut snapshot = this.create_snapshot_for_diagnostic();
                     let pattern_follows = snapshot
-                        .parse_pat_allow_top_alt(
+                        .parse_pat_no_top_guard(
                             None,
                             RecoverComma::Yes,
                             RecoverColon::Yes,
@@ -3315,43 +3315,37 @@ impl<'a> Parser<'a> {
 
     fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> {
         if self.token == token::OpenDelim(Delimiter::Parenthesis) {
-            // Detect and recover from `($pat if $cond) => $arm`.
             let left = self.token.span;
-            match self.parse_pat_allow_top_alt(
+            let pat = self.parse_pat_no_top_guard(
                 None,
                 RecoverComma::Yes,
                 RecoverColon::Yes,
                 CommaRecoveryMode::EitherTupleOrPipe,
-            ) {
-                Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)),
-                Err(err)
-                    if let prev_sp = self.prev_token.span
-                        && let true = self.eat_keyword(kw::If) =>
-                {
-                    // We know for certain we've found `($pat if` so far.
-                    let mut cond = match self.parse_match_guard_condition() {
-                        Ok(cond) => cond,
-                        Err(cond_err) => {
-                            cond_err.cancel();
-                            return Err(err);
-                        }
-                    };
-                    err.cancel();
-                    CondChecker::new(self).visit_expr(&mut cond);
-                    self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]);
-                    self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
-                    let right = self.prev_token.span;
-                    self.dcx().emit_err(errors::ParenthesesInMatchPat {
-                        span: vec![left, right],
-                        sugg: errors::ParenthesesInMatchPatSugg { left, right },
-                    });
-                    Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond)))
-                }
-                Err(err) => Err(err),
+            )?;
+            if let ast::PatKind::Paren(subpat) = &pat.kind
+                && let ast::PatKind::Guard(..) = &subpat.kind
+            {
+                // Detect and recover from `($pat if $cond) => $arm`.
+                // FIXME(guard_patterns): convert this to a normal guard instead
+                let span = pat.span;
+                let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() };
+                let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else {
+                    unreachable!()
+                };
+                self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
+                CondChecker::new(self).visit_expr(&mut cond);
+                let right = self.prev_token.span;
+                self.dcx().emit_err(errors::ParenthesesInMatchPat {
+                    span: vec![left, right],
+                    sugg: errors::ParenthesesInMatchPatSugg { left, right },
+                });
+                Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond)))
+            } else {
+                Ok((pat, self.parse_match_arm_guard()?))
             }
         } else {
             // Regular parser flow:
-            let pat = self.parse_pat_allow_top_alt(
+            let pat = self.parse_pat_no_top_guard(
                 None,
                 RecoverComma::Yes,
                 RecoverColon::Yes,
diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs
index 8fb6f85d0dd..752a52b382b 100644
--- a/compiler/rustc_parse/src/parser/nonterminal.rs
+++ b/compiler/rustc_parse/src/parser/nonterminal.rs
@@ -174,7 +174,7 @@ impl<'a> Parser<'a> {
             NonterminalKind::Pat(pat_kind) => {
                 NtPat(self.collect_tokens_no_attrs(|this| match pat_kind {
                     PatParam { .. } => this.parse_pat_no_top_alt(None, None),
-                    PatWithOr => this.parse_pat_allow_top_alt(
+                    PatWithOr => this.parse_pat_no_top_guard(
                         None,
                         RecoverComma::No,
                         RecoverColon::No,
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index e08b925f008..4cda887a02b 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -99,9 +99,34 @@ pub enum PatternLocation {
 impl<'a> Parser<'a> {
     /// Parses a pattern.
     ///
-    /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns
-    /// at the top level. Used when parsing the parameters of lambda expressions,
-    /// functions, function pointers, and `pat` macro fragments.
+    /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level.
+    /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor
+    /// `PatternNoTopAlt` (see below) are used.
+    pub fn parse_pat_allow_top_guard(
+        &mut self,
+        expected: Option<Expected>,
+        rc: RecoverComma,
+        ra: RecoverColon,
+        rt: CommaRecoveryMode,
+    ) -> PResult<'a, P<Pat>> {
+        let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?;
+
+        if self.eat_keyword(kw::If) {
+            let cond = self.parse_expr()?;
+            // Feature-gate guard patterns
+            self.psess.gated_spans.gate(sym::guard_patterns, cond.span);
+            let span = pat.span.to(cond.span);
+            Ok(self.mk_pat(span, PatKind::Guard(pat, cond)))
+        } else {
+            Ok(pat)
+        }
+    }
+
+    /// Parses a pattern.
+    ///
+    /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns
+    /// or guard patterns at the top level. Used when parsing the parameters of lambda
+    /// expressions, functions, function pointers, and `pat_param` macro fragments.
     pub fn parse_pat_no_top_alt(
         &mut self,
         expected: Option<Expected>,
@@ -112,25 +137,26 @@ impl<'a> Parser<'a> {
 
     /// Parses a pattern.
     ///
-    /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
-    /// Used for parsing patterns in all cases when `pat<no_top_alt>` is not used.
+    /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not
+    /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until
+    /// the next edition) and `let`, `if let`, and `while let` expressions.
     ///
     /// Note that after the FCP in <https://github.com/rust-lang/rust/issues/81415>,
     /// a leading vert is allowed in nested or-patterns, too. This allows us to
     /// simplify the grammar somewhat.
-    pub fn parse_pat_allow_top_alt(
+    pub fn parse_pat_no_top_guard(
         &mut self,
         expected: Option<Expected>,
         rc: RecoverComma,
         ra: RecoverColon,
         rt: CommaRecoveryMode,
     ) -> PResult<'a, P<Pat>> {
-        self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
+        self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat)
     }
 
     /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
     /// recovered).
-    fn parse_pat_allow_top_alt_inner(
+    fn parse_pat_no_top_guard_inner(
         &mut self,
         expected: Option<Expected>,
         rc: RecoverComma,
@@ -231,7 +257,7 @@ impl<'a> Parser<'a> {
         // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level
         // or-patterns so that we can detect when a user tries to use it. This allows us to print a
         // better error message.
-        let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner(
+        let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner(
             expected,
             rc,
             RecoverColon::No,
@@ -696,7 +722,7 @@ impl<'a> Parser<'a> {
         } else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
             // Parse `[pat, pat,...]` as a slice pattern.
             let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| {
-                p.parse_pat_allow_top_alt(
+                p.parse_pat_allow_top_guard(
                     None,
                     RecoverComma::No,
                     RecoverColon::No,
@@ -944,7 +970,7 @@ impl<'a> Parser<'a> {
         let open_paren = self.token.span;
 
         let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
-            p.parse_pat_allow_top_alt(
+            p.parse_pat_allow_top_guard(
                 None,
                 RecoverComma::No,
                 RecoverColon::No,
@@ -1361,7 +1387,7 @@ impl<'a> Parser<'a> {
         path: Path,
     ) -> PResult<'a, PatKind> {
         let (fields, _) = self.parse_paren_comma_seq(|p| {
-            p.parse_pat_allow_top_alt(
+            p.parse_pat_allow_top_guard(
                 None,
                 RecoverComma::No,
                 RecoverColon::No,
@@ -1396,7 +1422,7 @@ impl<'a> Parser<'a> {
         self.parse_builtin(|self_, _lo, ident| {
             Ok(match ident.name {
                 // builtin#deref(PAT)
-                sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt(
+                sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard(
                     None,
                     RecoverComma::Yes,
                     RecoverColon::Yes,
@@ -1671,7 +1697,7 @@ impl<'a> Parser<'a> {
             // Parsing a pattern of the form `fieldname: pat`.
             let fieldname = self.parse_field_name()?;
             self.bump();
-            let pat = self.parse_pat_allow_top_alt(
+            let pat = self.parse_pat_allow_top_guard(
                 None,
                 RecoverComma::No,
                 RecoverColon::No,
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 2f19a9b6b20..6a7029a8f1c 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -469,7 +469,7 @@ impl<'a> Parser<'a> {
             PathStyle::Pat
                 if let Ok(_) = self
                     .parse_paren_comma_seq(|p| {
-                        p.parse_pat_allow_top_alt(
+                        p.parse_pat_allow_top_guard(
                             None,
                             RecoverComma::No,
                             RecoverColon::No,
diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs
index b8f66a2b2ec..f2a37307cee 100644
--- a/compiler/rustc_passes/src/input_stats.rs
+++ b/compiler/rustc_passes/src/input_stats.rs
@@ -555,6 +555,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
             Slice,
             Rest,
             Never,
+            Guard,
             Paren,
             MacCall,
             Err
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index a7dc20a874b..818d4afffc6 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -999,6 +999,7 @@ symbols! {
         global_registration,
         globs,
         gt,
+        guard_patterns,
         half_open_range_patterns,
         half_open_range_patterns_in_slices,
         hash,
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index 802b571c510..3e53c0497cc 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -3577,34 +3577,44 @@ pub const fn discriminant_value<T>(_v: &T) -> <T as DiscriminantKind>::Discrimin
     unimplemented!()
 }
 
-extern "rust-intrinsic" {
-    /// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the
-    /// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs.
-    ///
-    /// `catch_fn` must not unwind.
-    ///
-    /// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign
-    /// unwinds). This function takes the data pointer and a pointer to the target- and
-    /// runtime-specific exception object that was caught.
-    ///
-    /// Note that in the case of a foreign unwinding operation, the exception object data may not be
-    /// safely usable from Rust, and should not be directly exposed via the standard library. To
-    /// prevent unsafe access, the library implementation may either abort the process or present an
-    /// opaque error type to the user.
-    ///
-    /// For more information, see the compiler's source, as well as the documentation for the stable
-    /// version of this intrinsic, `std::panic::catch_unwind`.
-    #[rustc_nounwind]
-    pub fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32;
-
-    /// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held
-    /// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`.
-    ///
-    /// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT`
-    /// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered
-    /// in ways that are not allowed for regular writes).
-    #[rustc_nounwind]
-    pub fn nontemporal_store<T>(ptr: *mut T, val: T);
+/// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the
+/// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs.
+///
+/// `catch_fn` must not unwind.
+///
+/// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign
+/// unwinds). This function takes the data pointer and a pointer to the target- and
+/// runtime-specific exception object that was caught.
+///
+/// Note that in the case of a foreign unwinding operation, the exception object data may not be
+/// safely usable from Rust, and should not be directly exposed via the standard library. To
+/// prevent unsafe access, the library implementation may either abort the process or present an
+/// opaque error type to the user.
+///
+/// For more information, see the compiler's source, as well as the documentation for the stable
+/// version of this intrinsic, `std::panic::catch_unwind`.
+#[rustc_intrinsic]
+#[rustc_intrinsic_must_be_overridden]
+#[rustc_nounwind]
+pub unsafe fn catch_unwind(
+    _try_fn: fn(*mut u8),
+    _data: *mut u8,
+    _catch_fn: fn(*mut u8, *mut u8),
+) -> i32 {
+    unreachable!()
+}
+
+/// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held
+/// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`.
+///
+/// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT`
+/// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered
+/// in ways that are not allowed for regular writes).
+#[rustc_intrinsic]
+#[rustc_intrinsic_must_be_overridden]
+#[rustc_nounwind]
+pub unsafe fn nontemporal_store<T>(_ptr: *mut T, _val: T) {
+    unreachable!()
 }
 
 /// See documentation of `<*const T>::offset_from` for details.
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index 9d26bf930a1..50a97579df7 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -234,7 +234,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
         // In the case of only two patterns, replacement adds net characters.
         | Ref(_, Mutability::Not)
         // Dealt with elsewhere.
-        | Or(_) | Paren(_) | Deref(_) => false,
+        | Or(_) | Paren(_) | Deref(_) | Guard(..) => false,
         // Transform `box x | ... | box y` into `box (x | y)`.
         //
         // The cases below until `Slice(...)` deal with *singleton* products.
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 84269fd44a1..7b11bf3b121 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -22,7 +22,7 @@ use crate::common::{
     UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir,
     output_base_dir, output_base_name, output_testname_unique,
 };
-use crate::compute_diff::{write_diff, write_filtered_diff};
+use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
 use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::read2::{Truncated, read2_abbreviated};
@@ -2295,17 +2295,31 @@ impl<'test> TestCx<'test> {
         match output_kind {
             TestOutput::Compile => {
                 if !self.props.dont_check_compiler_stdout {
-                    errors +=
-                        self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
+                    errors += self.compare_output(
+                        stdout_kind,
+                        &normalized_stdout,
+                        &proc_res.stdout,
+                        &expected_stdout,
+                    );
                 }
                 if !self.props.dont_check_compiler_stderr {
-                    errors +=
-                        self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
+                    errors += self.compare_output(
+                        stderr_kind,
+                        &normalized_stderr,
+                        &stderr,
+                        &expected_stderr,
+                    );
                 }
             }
             TestOutput::Run => {
-                errors += self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
-                errors += self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
+                errors += self.compare_output(
+                    stdout_kind,
+                    &normalized_stdout,
+                    &proc_res.stdout,
+                    &expected_stdout,
+                );
+                errors +=
+                    self.compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr);
             }
         }
         errors
@@ -2533,7 +2547,13 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn compare_output(&self, stream: &str, actual: &str, expected: &str) -> usize {
+    fn compare_output(
+        &self,
+        stream: &str,
+        actual: &str,
+        actual_unnormalized: &str,
+        expected: &str,
+    ) -> usize {
         let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) {
             // FIXME: We ignore the first line of SVG files
             // because the width parameter is non-deterministic.
@@ -2590,28 +2610,14 @@ impl<'test> TestCx<'test> {
             if expected.is_empty() {
                 println!("normalized {}:\n{}\n", stream, actual);
             } else {
-                println!("diff of {stream}:\n");
-                if let Some(diff_command) = self.config.diff_command.as_deref() {
-                    let mut args = diff_command.split_whitespace();
-                    let name = args.next().unwrap();
-                    match Command::new(name)
-                        .args(args)
-                        .args([&expected_path, &actual_path])
-                        .output()
-                    {
-                        Err(err) => {
-                            self.fatal(&format!(
-                                "failed to call custom diff command `{diff_command}`: {err}"
-                            ));
-                        }
-                        Ok(output) => {
-                            let output = String::from_utf8_lossy(&output.stdout);
-                            print!("{output}");
-                        }
-                    }
-                } else {
-                    print!("{}", write_diff(expected, actual, 3));
-                }
+                self.show_diff(
+                    stream,
+                    &expected_path,
+                    &actual_path,
+                    expected,
+                    actual,
+                    actual_unnormalized,
+                );
             }
         } else {
             // Delete non-revision .stderr/.stdout file if revisions are used.
@@ -2633,6 +2639,76 @@ impl<'test> TestCx<'test> {
         if self.config.bless { 0 } else { 1 }
     }
 
+    /// Returns whether to show the full stderr/stdout.
+    fn show_diff(
+        &self,
+        stream: &str,
+        expected_path: &Path,
+        actual_path: &Path,
+        expected: &str,
+        actual: &str,
+        actual_unnormalized: &str,
+    ) {
+        eprintln!("diff of {stream}:\n");
+        if let Some(diff_command) = self.config.diff_command.as_deref() {
+            let mut args = diff_command.split_whitespace();
+            let name = args.next().unwrap();
+            match Command::new(name).args(args).args([expected_path, actual_path]).output() {
+                Err(err) => {
+                    self.fatal(&format!(
+                        "failed to call custom diff command `{diff_command}`: {err}"
+                    ));
+                }
+                Ok(output) => {
+                    let output = String::from_utf8_lossy(&output.stdout);
+                    eprint!("{output}");
+                }
+            }
+        } else {
+            eprint!("{}", write_diff(expected, actual, 3));
+        }
+
+        // NOTE: argument order is important, we need `actual` to be on the left so the line number match up when we compare it to `actual_unnormalized` below.
+        let diff_results = make_diff(actual, expected, 0);
+
+        let (mut mismatches_normalized, mut mismatch_line_nos) = (String::new(), vec![]);
+        for hunk in diff_results {
+            let mut line_no = hunk.line_number;
+            for line in hunk.lines {
+                // NOTE: `Expected` is actually correct here, the argument order is reversed so our line numbers match up
+                if let DiffLine::Expected(normalized) = line {
+                    mismatches_normalized += &normalized;
+                    mismatches_normalized += "\n";
+                    mismatch_line_nos.push(line_no);
+                    line_no += 1;
+                }
+            }
+        }
+        let mut mismatches_unnormalized = String::new();
+        let diff_normalized = make_diff(actual, actual_unnormalized, 0);
+        for hunk in diff_normalized {
+            if mismatch_line_nos.contains(&hunk.line_number) {
+                for line in hunk.lines {
+                    if let DiffLine::Resulting(unnormalized) = line {
+                        mismatches_unnormalized += &unnormalized;
+                        mismatches_unnormalized += "\n";
+                    }
+                }
+            }
+        }
+
+        let normalized_diff = make_diff(&mismatches_normalized, &mismatches_unnormalized, 0);
+        // HACK: instead of checking if each hunk is empty, this only checks if the whole input is empty. we should be smarter about this so we don't treat added or removed output as normalized.
+        if !normalized_diff.is_empty()
+            && !mismatches_unnormalized.is_empty()
+            && !mismatches_normalized.is_empty()
+        {
+            eprintln!("Note: some mismatched output was normalized before being compared");
+            // FIXME: respect diff_command
+            eprint!("{}", write_diff(&mismatches_unnormalized, &mismatches_normalized, 0));
+        }
+    }
+
     fn check_and_prune_duplicate_outputs(
         &self,
         proc_res: &ProcRes,
diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs
index 961a1602986..030ca5ebb24 100644
--- a/src/tools/compiletest/src/runtest/coverage.rs
+++ b/src/tools/compiletest/src/runtest/coverage.rs
@@ -39,8 +39,12 @@ impl<'test> TestCx<'test> {
         let expected_coverage_dump = self.load_expected_output(kind);
         let actual_coverage_dump = self.normalize_output(&proc_res.stdout, &[]);
 
-        let coverage_dump_errors =
-            self.compare_output(kind, &actual_coverage_dump, &expected_coverage_dump);
+        let coverage_dump_errors = self.compare_output(
+            kind,
+            &actual_coverage_dump,
+            &proc_res.stdout,
+            &expected_coverage_dump,
+        );
 
         if coverage_dump_errors > 0 {
             self.fatal_proc_rec(
@@ -135,8 +139,12 @@ impl<'test> TestCx<'test> {
                 self.fatal_proc_rec(&err, &proc_res);
             });
 
-        let coverage_errors =
-            self.compare_output(kind, &normalized_actual_coverage, &expected_coverage);
+        let coverage_errors = self.compare_output(
+            kind,
+            &normalized_actual_coverage,
+            &proc_res.stdout,
+            &expected_coverage,
+        );
 
         if coverage_errors > 0 {
             self.fatal_proc_rec(
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index bb747c68029..172b1e32aad 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -100,7 +100,7 @@ impl TestCx<'_> {
                 )
             });
 
-            errors += self.compare_output("fixed", &fixed_code, &expected_fixed);
+            errors += self.compare_output("fixed", &fixed_code, &fixed_code, &expected_fixed);
         } else if !expected_fixed.is_empty() {
             panic!(
                 "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index 6fe2d4a8520..7b4730eadc8 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -48,7 +48,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
         | ast::PatKind::MacCall(..)
         | ast::PatKind::Slice(..)
         | ast::PatKind::Path(..)
-        | ast::PatKind::Range(..) => false,
+        | ast::PatKind::Range(..)
+        | ast::PatKind::Guard(..) => false,
         ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1,
         ast::PatKind::TupleStruct(_, ref path, ref subpats) => {
             path.segments.len() <= 1 && subpats.len() <= 1
@@ -338,8 +339,9 @@ impl Rewrite for Pat {
                         .max_width_error(shape.width, self.span)?,
                 )
                 .map(|inner_pat| format!("({})", inner_pat)),
-            PatKind::Err(_) => Err(RewriteError::Unknown),
+            PatKind::Guard(..) => Ok(context.snippet(self.span).to_string()),
             PatKind::Deref(_) => Err(RewriteError::Unknown),
+            PatKind::Err(_) => Err(RewriteError::Unknown),
         }
     }
 }
diff --git a/src/tools/rustfmt/tests/target/guard_patterns.rs b/src/tools/rustfmt/tests/target/guard_patterns.rs
new file mode 100644
index 00000000000..2e4667b916c
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/guard_patterns.rs
@@ -0,0 +1,12 @@
+#![feature(guard_patterns)]
+
+fn main() {
+    match user.subscription_plan() {
+        (Plan::Regular if user.credit() >= 100) | (Plan::Premium if user.credit() >= 80) => {
+            // Complete the transaction.
+        }
+        _ => {
+            // The user doesn't have enough credit, return an error message.
+        }
+    }
+}
diff --git a/tests/ui/assign-imm-local-twice.rs b/tests/ui/assign-imm-local-twice.rs
deleted file mode 100644
index b2dfeb564d9..00000000000
--- a/tests/ui/assign-imm-local-twice.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-fn test() {
-    let v: isize;
-    //~^ HELP consider making this binding mutable
-    //~| SUGGESTION mut
-    v = 1; //~ NOTE first assignment
-    println!("v={}", v);
-    v = 2; //~ ERROR cannot assign twice to immutable variable
-           //~| NOTE cannot assign twice to immutable
-    println!("v={}", v);
-}
-
-fn main() {
-}
diff --git a/tests/ui/assoc-lang-items.rs b/tests/ui/assoc-lang-items.rs
deleted file mode 100644
index 23453d201a7..00000000000
--- a/tests/ui/assoc-lang-items.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-#![feature(lang_items)]
-
-trait Foo {
-    #[lang = "dummy_lang_item_1"] //~ ERROR definition
-    fn foo() {}
-
-    #[lang = "dummy_lang_item_2"] //~ ERROR definition
-    fn bar();
-
-    #[lang = "dummy_lang_item_3"] //~ ERROR definition
-    type MyType;
-}
-
-struct Bar;
-
-impl Bar {
-    #[lang = "dummy_lang_item_4"] //~ ERROR definition
-    fn test() {}
-}
-
-fn main() {}
diff --git a/tests/ui/assoc-oddities-3.rs b/tests/ui/assoc-oddities-3.rs
deleted file mode 100644
index ffde2ccf786..00000000000
--- a/tests/ui/assoc-oddities-3.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//@ run-pass
-
-fn that_odd_parse(c: bool, n: usize) -> u32 {
-    let x = 2;
-    let a = [1, 2, 3, 4];
-    let b = [5, 6, 7, 7];
-    x + if c { a } else { b }[n]
-}
-
-fn main() {
-    assert_eq!(4, that_odd_parse(true, 1));
-    assert_eq!(8, that_odd_parse(false, 1));
-}
diff --git a/tests/ui/atomic-from-mut-not-available.rs b/tests/ui/atomic-from-mut-not-available.rs
deleted file mode 100644
index 8326187838a..00000000000
--- a/tests/ui/atomic-from-mut-not-available.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@ only-x86
-//@ only-linux
-
-fn main() {
-    core::sync::atomic::AtomicU64::from_mut(&mut 0u64);
-    //~^ ERROR: no function or associated item named `from_mut` found for struct `AtomicU64`
-}
diff --git a/tests/ui/borrowck/assign-imm-local-twice.fixed b/tests/ui/borrowck/assign-imm-local-twice.fixed
new file mode 100644
index 00000000000..dca994141bb
--- /dev/null
+++ b/tests/ui/borrowck/assign-imm-local-twice.fixed
@@ -0,0 +1,21 @@
+//! Check that we do not allow assigning twice to an immutable variable. This test also checks a
+//! few pieces of borrowck diagnostics:
+//!
+//! - A multipart borrowck diagnostics that points out the first assignment to an immutable
+//!   variable, alongside violating assignments that follow subsequently.
+//! - A suggestion diagnostics to make the immutable binding mutable.
+
+//@ run-rustfix
+
+fn main() {
+    let mut v: isize;
+    //~^ HELP consider making this binding mutable
+    //~| SUGGESTION mut
+    v = 1;
+    //~^ NOTE first assignment
+    println!("v={}", v);
+    v = 2;
+    //~^ ERROR cannot assign twice to immutable variable
+    //~| NOTE cannot assign twice to immutable
+    println!("v={}", v);
+}
diff --git a/tests/ui/borrowck/assign-imm-local-twice.rs b/tests/ui/borrowck/assign-imm-local-twice.rs
new file mode 100644
index 00000000000..43437fa69fa
--- /dev/null
+++ b/tests/ui/borrowck/assign-imm-local-twice.rs
@@ -0,0 +1,21 @@
+//! Check that we do not allow assigning twice to an immutable variable. This test also checks a
+//! few pieces of borrowck diagnostics:
+//!
+//! - A multipart borrowck diagnostics that points out the first assignment to an immutable
+//!   variable, alongside violating assignments that follow subsequently.
+//! - A suggestion diagnostics to make the immutable binding mutable.
+
+//@ run-rustfix
+
+fn main() {
+    let v: isize;
+    //~^ HELP consider making this binding mutable
+    //~| SUGGESTION mut
+    v = 1;
+    //~^ NOTE first assignment
+    println!("v={}", v);
+    v = 2;
+    //~^ ERROR cannot assign twice to immutable variable
+    //~| NOTE cannot assign twice to immutable
+    println!("v={}", v);
+}
diff --git a/tests/ui/assign-imm-local-twice.stderr b/tests/ui/borrowck/assign-imm-local-twice.stderr
index fda3aa3de1b..0a2138d0b96 100644
--- a/tests/ui/assign-imm-local-twice.stderr
+++ b/tests/ui/borrowck/assign-imm-local-twice.stderr
@@ -1,9 +1,9 @@
 error[E0384]: cannot assign twice to immutable variable `v`
-  --> $DIR/assign-imm-local-twice.rs:7:5
+  --> $DIR/assign-imm-local-twice.rs:17:5
    |
 LL |     v = 1;
    |     ----- first assignment to `v`
-LL |     println!("v={}", v);
+...
 LL |     v = 2;
    |     ^^^^^ cannot assign twice to immutable variable
    |
diff --git a/tests/ui/assign-assign.rs b/tests/ui/codegen/assign-expr-unit-type.rs
index 002393f5bff..b4268b5e09a 100644
--- a/tests/ui/assign-assign.rs
+++ b/tests/ui/codegen/assign-expr-unit-type.rs
@@ -1,5 +1,11 @@
+//! Regression test for [Using the result of an assignment expression results in an LLVM assert
+//! #483][issue-483]. This test checks that assignment expressions produce a unit type, and is
+//! properly lowered to LLVM IR such that it does not trigger an LLVM assertion. This test was added
+//! *really* early, back in 2011.
+//!
+//! [issue-483]: https://github.com/rust-lang/rust/issues/483
+
 //@ run-pass
-// Issue 483 - Assignment expressions result in nil
 
 fn test_assign() {
     let mut x: isize;
@@ -27,4 +33,7 @@ fn test_assign_op() {
     assert_eq!(z, ());
 }
 
-pub fn main() { test_assign(); test_assign_op(); }
+pub fn main() {
+    test_assign();
+    test_assign_op();
+}
diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs
new file mode 100644
index 00000000000..929e8ef3181
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs
@@ -0,0 +1,46 @@
+fn match_guards_still_work() {
+    match 0 {
+        0 if guard(0) => {},
+        _ => {},
+    }
+}
+
+fn other_guards_dont() {
+    match 0 {
+        (0 if guard(0)) => {},
+        //~^ ERROR unexpected parentheses surrounding `match` arm pattern
+        _ => {},
+    }
+
+    match 0 {
+        (0 if guard(0)) | 1 => {},
+        //~^ ERROR: guard patterns are experimental
+        _ => {},
+    }
+
+    let ((x if guard(x)) | x) = 0;
+    //~^ ERROR: guard patterns are experimental
+    //~| ERROR: cannot find value `x`
+
+    if let (x if guard(x)) = 0 {}
+    //~^ ERROR: guard patterns are experimental
+    //~| WARN: irrefutable
+
+    while let (x if guard(x)) = 0 {}
+    //~^ ERROR: guard patterns are experimental
+    //~| WARN: irrefutable
+
+    #[cfg(FALSE)]
+    while let (x if guard(x)) = 0 {}
+    //~^ ERROR: guard patterns are experimental
+}
+
+fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
+//~^ ERROR: guard patterns are experimental
+//~| ERROR: cannot find value `x`
+
+fn guard<T>(x: T) -> bool {
+    unimplemented!()
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
new file mode 100644
index 00000000000..0613b5c95a4
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
@@ -0,0 +1,119 @@
+error: unexpected parentheses surrounding `match` arm pattern
+  --> $DIR/feature-gate-guard-patterns.rs:10:9
+   |
+LL |         (0 if guard(0)) => {},
+   |         ^             ^
+   |
+help: remove parentheses surrounding the pattern
+   |
+LL -         (0 if guard(0)) => {},
+LL +         0 if guard(0) => {},
+   |
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/feature-gate-guard-patterns.rs:21:22
+   |
+LL |     let ((x if guard(x)) | x) = 0;
+   |                      ^ not found in this scope
+
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/feature-gate-guard-patterns.rs:38:45
+   |
+LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
+   |                                             ^
+   |
+help: the binding `x` is available in a different scope in the same function
+  --> $DIR/feature-gate-guard-patterns.rs:21:11
+   |
+LL |     let ((x if guard(x)) | x) = 0;
+   |           ^
+
+error[E0658]: guard patterns are experimental
+  --> $DIR/feature-gate-guard-patterns.rs:16:15
+   |
+LL |         (0 if guard(0)) | 1 => {},
+   |               ^^^^^^^^
+   |
+   = note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
+   = help: add `#![feature(guard_patterns)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: consider using match arm guards
+
+error[E0658]: guard patterns are experimental
+  --> $DIR/feature-gate-guard-patterns.rs:21:16
+   |
+LL |     let ((x if guard(x)) | x) = 0;
+   |                ^^^^^^^^
+   |
+   = note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
+   = help: add `#![feature(guard_patterns)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: consider using match arm guards
+
+error[E0658]: guard patterns are experimental
+  --> $DIR/feature-gate-guard-patterns.rs:25:18
+   |
+LL |     if let (x if guard(x)) = 0 {}
+   |                  ^^^^^^^^
+   |
+   = note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
+   = help: add `#![feature(guard_patterns)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: consider using match arm guards
+
+error[E0658]: guard patterns are experimental
+  --> $DIR/feature-gate-guard-patterns.rs:29:21
+   |
+LL |     while let (x if guard(x)) = 0 {}
+   |                     ^^^^^^^^
+   |
+   = note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
+   = help: add `#![feature(guard_patterns)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: consider using match arm guards
+
+error[E0658]: guard patterns are experimental
+  --> $DIR/feature-gate-guard-patterns.rs:34:21
+   |
+LL |     while let (x if guard(x)) = 0 {}
+   |                     ^^^^^^^^
+   |
+   = note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
+   = help: add `#![feature(guard_patterns)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: consider using match arm guards
+
+error[E0658]: guard patterns are experimental
+  --> $DIR/feature-gate-guard-patterns.rs:38:39
+   |
+LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
+   |                                       ^^^^^^^^
+   |
+   = note: see issue #129967 <https://github.com/rust-lang/rust/issues/129967> for more information
+   = help: add `#![feature(guard_patterns)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+   = help: consider using match arm guards
+
+warning: irrefutable `if let` pattern
+  --> $DIR/feature-gate-guard-patterns.rs:25:8
+   |
+LL |     if let (x if guard(x)) = 0 {}
+   |        ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this pattern will always match, so the `if let` is useless
+   = help: consider replacing the `if let` with a `let`
+   = note: `#[warn(irrefutable_let_patterns)]` on by default
+
+warning: irrefutable `while let` pattern
+  --> $DIR/feature-gate-guard-patterns.rs:29:11
+   |
+LL |     while let (x if guard(x)) = 0 {}
+   |           ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this pattern will always match, so the loop will never exit
+   = help: consider instead using a `loop { ... }` with a `let` inside it
+
+error: aborting due to 9 previous errors; 2 warnings emitted
+
+Some errors have detailed explanations: E0425, E0658.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/tests/ui/lang-items/assoc-lang-items.rs b/tests/ui/lang-items/assoc-lang-items.rs
new file mode 100644
index 00000000000..460d3ed2326
--- /dev/null
+++ b/tests/ui/lang-items/assoc-lang-items.rs
@@ -0,0 +1,35 @@
+//! Check that associated items can be marked as lang items, so that they don't have to be looked up
+//! by name or by definition order indirectly.
+//!
+//! This test is not *quite* high-fidelity: it checks that you can use lang items on associated
+//! items by looking at the error message *as a proxy*. That is, the error message is about
+//! undefined lang items and not invalid attribute target, indicating that it has reached lang item
+//! machinery (which is relying on knowing the implementation detail). However, it's annoying to
+//! write a full-fidelity test for this, so I think this is acceptable even though it's not *great*.
+//!
+//! This was implemented in <https://github.com/rust-lang/rust/pull/72559> to help with
+//! <https://github.com/rust-lang/rust/issues/70718>, which is itself relevant for e.g. `Fn::Output`
+//! or `Future::Output` or specific use cases like [Use `T`'s discriminant type in
+//! `mem::Discriminant<T>` instead of `u64`](https://github.com/rust-lang/rust/pull/70705).
+
+#![feature(lang_items)]
+
+trait Foo {
+    #[lang = "dummy_lang_item_1"] //~ ERROR definition
+    fn foo() {}
+
+    #[lang = "dummy_lang_item_2"] //~ ERROR definition
+    fn bar();
+
+    #[lang = "dummy_lang_item_3"] //~ ERROR definition
+    type MyType;
+}
+
+struct Bar;
+
+impl Bar {
+    #[lang = "dummy_lang_item_4"] //~ ERROR definition
+    fn test() {}
+}
+
+fn main() {}
diff --git a/tests/ui/assoc-lang-items.stderr b/tests/ui/lang-items/assoc-lang-items.stderr
index 59aec8e3fdc..7e61fea449b 100644
--- a/tests/ui/assoc-lang-items.stderr
+++ b/tests/ui/lang-items/assoc-lang-items.stderr
@@ -1,23 +1,23 @@
 error[E0522]: definition of an unknown lang item: `dummy_lang_item_1`
-  --> $DIR/assoc-lang-items.rs:4:5
+  --> $DIR/assoc-lang-items.rs:18:5
    |
 LL |     #[lang = "dummy_lang_item_1"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_1`
 
 error[E0522]: definition of an unknown lang item: `dummy_lang_item_2`
-  --> $DIR/assoc-lang-items.rs:7:5
+  --> $DIR/assoc-lang-items.rs:21:5
    |
 LL |     #[lang = "dummy_lang_item_2"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_2`
 
 error[E0522]: definition of an unknown lang item: `dummy_lang_item_3`
-  --> $DIR/assoc-lang-items.rs:10:5
+  --> $DIR/assoc-lang-items.rs:24:5
    |
 LL |     #[lang = "dummy_lang_item_3"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_3`
 
 error[E0522]: definition of an unknown lang item: `dummy_lang_item_4`
-  --> $DIR/assoc-lang-items.rs:17:5
+  --> $DIR/assoc-lang-items.rs:31:5
    |
 LL |     #[lang = "dummy_lang_item_4"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_4`
diff --git a/tests/ui/parser/assoc/assoc-oddities-3.rs b/tests/ui/parser/assoc/assoc-oddities-3.rs
new file mode 100644
index 00000000000..1a41c4be023
--- /dev/null
+++ b/tests/ui/parser/assoc/assoc-oddities-3.rs
@@ -0,0 +1,41 @@
+//! Check that braces has the expected precedence in relation to index op and some arithmetic
+//! bin-ops involving nested braces.
+//!
+//! This is a regression test for [Wrapping expr in curly braces changes the operator precedence
+//! #28777](https://github.com/rust-lang/rust/issues/28777), which was fixed by
+//! <https://github.com/rust-lang/rust/pull/30375>.
+
+//@ run-pass
+
+fn that_odd_parse(c: bool, n: usize) -> u32 {
+    let x = 2;
+    let a = [1, 2, 3, 4];
+    let b = [5, 6, 7, 7];
+    x + if c { a } else { b }[n]
+}
+
+/// See [Wrapping expr in curly braces changes the operator precedence
+/// #28777](https://github.com/rust-lang/rust/issues/28777). This was fixed by
+/// <https://github.com/rust-lang/rust/pull/30375>. #30375 added the `that_odd_parse` example above,
+/// but that is not *quite* the same original example as reported in #28777, so we also include the
+/// original example here.
+fn check_issue_28777() {
+    // Before #30375 fixed the precedence...
+
+    // ... `v1` evaluated to 9, indicating a parse of `(1 + 2) * 3`, while
+    let v1 = { 1 + { 2 } * { 3 } };
+
+    // `v2` evaluated to 7, indicating a parse of `1 + (2 * 3)`.
+    let v2 = 1 + { 2 } * { 3 };
+
+    // Check that both now evaluate to 7, as was fixed by #30375.
+    assert_eq!(v1, 7);
+    assert_eq!(v2, 7);
+}
+
+fn main() {
+    assert_eq!(4, that_odd_parse(true, 1));
+    assert_eq!(8, that_odd_parse(false, 1));
+
+    check_issue_28777();
+}
diff --git a/tests/ui/parser/issues/issue-72373.rs b/tests/ui/parser/issues/issue-72373.rs
index 4da6061c27f..ed88d53539b 100644
--- a/tests/ui/parser/issues/issue-72373.rs
+++ b/tests/ui/parser/issues/issue-72373.rs
@@ -3,7 +3,7 @@ fn foo(c: &[u32], n: u32) -> u32 {
         [h, ..] if h > n => 0,
         [h, ..] if h == n => 1,
         [h, ref ts..] => foo(c, n - h) + foo(ts, n),
-        //~^ ERROR expected one of `,`, `@`, `]`, or `|`, found `..`
+        //~^ ERROR expected one of `,`, `@`, `]`, `if`, or `|`, found `..`
         [] => 0,
     }
 }
diff --git a/tests/ui/parser/issues/issue-72373.stderr b/tests/ui/parser/issues/issue-72373.stderr
index c596c6abda5..d566d6f5fd1 100644
--- a/tests/ui/parser/issues/issue-72373.stderr
+++ b/tests/ui/parser/issues/issue-72373.stderr
@@ -1,8 +1,8 @@
-error: expected one of `,`, `@`, `]`, or `|`, found `..`
+error: expected one of `,`, `@`, `]`, `if`, or `|`, found `..`
   --> $DIR/issue-72373.rs:5:19
    |
 LL |         [h, ref ts..] => foo(c, n - h) + foo(ts, n),
-   |                   ^^ expected one of `,`, `@`, `]`, or `|`
+   |                   ^^ expected one of `,`, `@`, `]`, `if`, or `|`
    |
 help: if you meant to bind the contents of the rest of the array pattern into `ts`, use `@`
    |
diff --git a/tests/ui/parser/misspelled-keywords/ref.stderr b/tests/ui/parser/misspelled-keywords/ref.stderr
index b8b52702314..398d9d6bb99 100644
--- a/tests/ui/parser/misspelled-keywords/ref.stderr
+++ b/tests/ui/parser/misspelled-keywords/ref.stderr
@@ -1,8 +1,8 @@
-error: expected one of `)`, `,`, `@`, or `|`, found `list`
+error: expected one of `)`, `,`, `@`, `if`, or `|`, found `list`
   --> $DIR/ref.rs:4:19
    |
 LL |         Some(refe list) => println!("{list:?}"),
-   |                   ^^^^ expected one of `)`, `,`, `@`, or `|`
+   |                   ^^^^ expected one of `)`, `,`, `@`, `if`, or `|`
    |
 help: there is a keyword `ref` with a similar name
    |
diff --git a/tests/ui/parser/pat-lt-bracket-7.rs b/tests/ui/parser/pat-lt-bracket-7.rs
index 327aef5ad15..abaeb4c83c0 100644
--- a/tests/ui/parser/pat-lt-bracket-7.rs
+++ b/tests/ui/parser/pat-lt-bracket-7.rs
@@ -3,7 +3,7 @@ fn main() {
     let foo = core::iter::empty();
 
     for Thing(x[]) in foo {}
-    //~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[`
+    //~^ ERROR: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
 }
 
 const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
diff --git a/tests/ui/parser/pat-lt-bracket-7.stderr b/tests/ui/parser/pat-lt-bracket-7.stderr
index 004dcfb2a7b..cc457a4e64e 100644
--- a/tests/ui/parser/pat-lt-bracket-7.stderr
+++ b/tests/ui/parser/pat-lt-bracket-7.stderr
@@ -1,10 +1,10 @@
-error: expected one of `)`, `,`, `@`, or `|`, found `[`
+error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
   --> $DIR/pat-lt-bracket-7.rs:5:16
    |
 LL |     for Thing(x[]) in foo {}
    |                ^
    |                |
-   |                expected one of `)`, `,`, `@`, or `|`
+   |                expected one of `)`, `,`, `@`, `if`, or `|`
    |                help: missing `,`
 
 error[E0308]: mismatched types
diff --git a/tests/ui/parser/recover/recover-pat-exprs.rs b/tests/ui/parser/recover/recover-pat-exprs.rs
index e5e25df0c01..a78bb82828d 100644
--- a/tests/ui/parser/recover/recover-pat-exprs.rs
+++ b/tests/ui/parser/recover/recover-pat-exprs.rs
@@ -27,7 +27,7 @@ fn array_indexing() {
     { let x[0, 1, 2]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
     { let x[0; 20]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
     { let x[]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
-    { let (x[]); } //~ error: expected one of `)`, `,`, `@`, or `|`, found `[`
+    { let (x[]); } //~ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
     //~^ missing `,`
 }
 
@@ -95,12 +95,12 @@ fn main() {
         f?() => (),
         //~^ error: expected a pattern, found an expression
         (_ + 1) => (),
-        //~^ error: expected one of `)`, `,`, or `|`, found `+`
+        //~^ error: expected one of `)`, `,`, `if`, or `|`, found `+`
     }
 
     let 1 + 1 = 2;
     //~^ error: expected a pattern, found an expression
 
     let b = matches!(x, (x * x | x.f()) | x[0]);
-    //~^ error: expected one of `)`, `,`, `@`, or `|`, found `*`
+    //~^ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*`
 }
diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr
index 041dfd647ad..281eeced402 100644
--- a/tests/ui/parser/recover/recover-pat-exprs.stderr
+++ b/tests/ui/parser/recover/recover-pat-exprs.stderr
@@ -213,13 +213,13 @@ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[`
 LL |     { let x[]; }
    |            ^ expected one of `:`, `;`, `=`, `@`, or `|`
 
-error: expected one of `)`, `,`, `@`, or `|`, found `[`
+error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[`
   --> $DIR/recover-pat-exprs.rs:30:13
    |
 LL |     { let (x[]); }
    |             ^
    |             |
-   |             expected one of `)`, `,`, `@`, or `|`
+   |             expected one of `)`, `,`, `@`, `if`, or `|`
    |             help: missing `,`
 
 error: expected a pattern, found an expression
@@ -611,11 +611,11 @@ LL |         x.sqrt() @ .. => (),
    |
    = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x`
 
-error: expected one of `)`, `,`, or `|`, found `+`
+error: expected one of `)`, `,`, `if`, or `|`, found `+`
   --> $DIR/recover-pat-exprs.rs:97:12
    |
 LL |         (_ + 1) => (),
-   |            ^ expected one of `)`, `,`, or `|`
+   |            ^ expected one of `)`, `,`, `if`, or `|`
 
 error: expected a pattern, found an expression
   --> $DIR/recover-pat-exprs.rs:81:9
@@ -772,11 +772,11 @@ LL |     let 1 + 1 = 2;
    |
    = note: arbitrary expressions are not allowed in patterns: <https://doc.rust-lang.org/book/ch19-00-patterns.html>
 
-error: expected one of `)`, `,`, `@`, or `|`, found `*`
+error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*`
   --> $DIR/recover-pat-exprs.rs:104:28
    |
 LL |     let b = matches!(x, (x * x | x.f()) | x[0]);
-   |                            ^ expected one of `)`, `,`, `@`, or `|`
+   |                            ^ expected one of `)`, `,`, `@`, `if`, or `|`
   --> $SRC_DIR/core/src/macros/mod.rs:LL:COL
    |
    = note: while parsing argument for this `pat` macro fragment
diff --git a/tests/ui/parser/recover/recover-pat-wildcards.rs b/tests/ui/parser/recover/recover-pat-wildcards.rs
index f506e2223d6..d4d28ce6358 100644
--- a/tests/ui/parser/recover/recover-pat-wildcards.rs
+++ b/tests/ui/parser/recover/recover-pat-wildcards.rs
@@ -8,7 +8,7 @@ fn a() {
 
 fn b() {
     match 2 {
-        (_ % 4) => () //~ error: expected one of `)`, `,`, or `|`, found `%`
+        (_ % 4) => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `%`
     }
 }
 
@@ -40,7 +40,7 @@ fn f() {
 
 fn g() {
     match 7 {
-        (_ * 0)..5 => () //~ error: expected one of `)`, `,`, or `|`, found `*`
+        (_ * 0)..5 => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `*`
     }
 }
 
diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr
index 81a9920f6a2..f939e513370 100644
--- a/tests/ui/parser/recover/recover-pat-wildcards.stderr
+++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr
@@ -4,11 +4,11 @@ error: expected one of `=>`, `if`, or `|`, found `+`
 LL |         _ + 1 => ()
    |           ^ expected one of `=>`, `if`, or `|`
 
-error: expected one of `)`, `,`, or `|`, found `%`
+error: expected one of `)`, `,`, `if`, or `|`, found `%`
   --> $DIR/recover-pat-wildcards.rs:11:12
    |
 LL |         (_ % 4) => ()
-   |            ^ expected one of `)`, `,`, or `|`
+   |            ^ expected one of `)`, `,`, `if`, or `|`
 
 error: expected one of `=>`, `if`, or `|`, found `.`
   --> $DIR/recover-pat-wildcards.rs:17:10
@@ -47,11 +47,11 @@ error: expected one of `=>`, `if`, or `|`, found reserved identifier `_`
 LL |         0..._ => ()
    |             ^ expected one of `=>`, `if`, or `|`
 
-error: expected one of `)`, `,`, or `|`, found `*`
+error: expected one of `)`, `,`, `if`, or `|`, found `*`
   --> $DIR/recover-pat-wildcards.rs:43:12
    |
 LL |         (_ * 0)..5 => ()
-   |            ^ expected one of `)`, `,`, or `|`
+   |            ^ expected one of `)`, `,`, `if`, or `|`
 
 error: expected one of `=>`, `if`, or `|`, found `(`
   --> $DIR/recover-pat-wildcards.rs:49:11
diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr
index da8f4ca5f0c..6ce8f6d31a0 100644
--- a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr
+++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr
@@ -6,11 +6,11 @@ LL |     let a: u8 @ b = 0;
    |          |
    |          while parsing the type for `a`
 
-error: expected one of `)`, `,`, `@`, or `|`, found `:`
+error: expected one of `)`, `,`, `@`, `if`, or `|`, found `:`
   --> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15
    |
 LL |     let a @ (b: u8);
-   |               ^ expected one of `)`, `,`, `@`, or `|`
+   |               ^ expected one of `)`, `,`, `@`, `if`, or `|`
    |
    = note: type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
 
diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs b/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs
new file mode 100644
index 00000000000..76681f45bb3
--- /dev/null
+++ b/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs
@@ -0,0 +1,20 @@
+//@ run-pass
+//! Tests that the addition of guard patterns does not change the behavior of the `pat` macro
+//! fragment.
+#![feature(guard_patterns)]
+#![allow(incomplete_features)]
+
+macro_rules! has_guard {
+    ($p:pat) => {
+        false
+    };
+    ($p:pat if $e:expr) => {
+        true
+    };
+}
+
+fn main() {
+    assert_eq!(has_guard!(Some(_)), false);
+    assert_eq!(has_guard!(Some(_) if true), true);
+    assert_eq!(has_guard!((Some(_) if true)), false);
+}
diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr
new file mode 100644
index 00000000000..109985c0052
--- /dev/null
+++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr
@@ -0,0 +1,13 @@
+error[E0658]: use of unstable library feature `atomic_from_mut`
+  --> $DIR/atomic-from-mut-not-available.rs:24:5
+   |
+LL |     core::sync::atomic::AtomicU64::from_mut(&mut 0u64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #76314 <https://github.com/rust-lang/rust/issues/76314> for more information
+   = help: add `#![feature(atomic_from_mut)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/atomic-from-mut-not-available.stderr b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr
index a4514524f48..47b17f9fcd7 100644
--- a/tests/ui/atomic-from-mut-not-available.stderr
+++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr
@@ -1,5 +1,5 @@
 error[E0599]: no function or associated item named `from_mut` found for struct `AtomicU64` in the current scope
-  --> $DIR/atomic-from-mut-not-available.rs:5:36
+  --> $DIR/atomic-from-mut-not-available.rs:24:36
    |
 LL |     core::sync::atomic::AtomicU64::from_mut(&mut 0u64);
    |                                    ^^^^^^^^ function or associated item not found in `AtomicU64`
diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs
new file mode 100644
index 00000000000..7e9de3570eb
--- /dev/null
+++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs
@@ -0,0 +1,27 @@
+//! This test exercises the combined effect of the `cfg(target_has_atomic_equal_alignment = "...")`
+//! implementation in the compiler plus usage of said `cfg(target_has_atomic_equal_alignment)` in
+//! `core` for the `Atomic64::from_mut` API.
+//!
+//! This test is a basic smoke test: that `AtomicU64::from_mut` is gated by
+//! `#[cfg(target_has_atomic_equal_alignment = "8")]`, which is only available on platforms where
+//! `AtomicU64` has the same alignment as `u64`. This is notably *not* satisfied by `x86_32`, where
+//! they have differing alignments. Thus, `AtomicU64::from_mut` should *not* be available on
+//! `x86_32` linux and should report assoc item not found, if the `cfg` is working correctly.
+//! Conversely, `AtomicU64::from_mut` *should* be available on `x86_64` linux where the alignment
+//! matches.
+
+//@ revisions: alignment_mismatch alignment_matches
+
+// This should fail on 32-bit x86 linux...
+//@[alignment_mismatch] only-x86
+//@[alignment_mismatch] only-linux
+
+// ... but pass on 64-bit x86_64 linux.
+//@[alignment_matches] only-x86_64
+//@[alignment_matches] only-linux
+
+fn main() {
+    core::sync::atomic::AtomicU64::from_mut(&mut 0u64);
+    //[alignment_mismatch]~^ ERROR no function or associated item named `from_mut` found for struct `AtomicU64`
+    //[alignment_matches]~^^ ERROR use of unstable library feature `atomic_from_mut`
+}