about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-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_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
15 files changed, 101 insertions, 53 deletions
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_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,