diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 114b9835b98c..ab48a2899a75 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -611,7 +611,7 @@ pub fn to_ty(&self) -> Option
> {
/// Walk top-down and call `it` in each place where a pattern occurs
/// starting with the root pattern `walk` is called on. If `it` returns
/// false then we will descend no further but siblings will be processed.
- pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) {
+ pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) {
if !it(self) {
return;
}
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index aa211a8f3c29..1b682d0cf8ae 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -799,7 +799,14 @@ fn visit_expr(&mut self, expr: &'ast Expr) {
fn visit_pat(&mut self, p: &'ast Pat) {
let prev = self.diag_metadata.current_pat;
self.diag_metadata.current_pat = Some(p);
- visit::walk_pat(self, p);
+
+ if let PatKind::Guard(subpat, _) = &p.kind {
+ // We walk the guard expression in `resolve_pattern_inner`. Don't resolve it twice.
+ self.visit_pat(subpat);
+ } else {
+ visit::walk_pat(self, p);
+ }
+
self.diag_metadata.current_pat = prev;
}
fn visit_local(&mut self, local: &'ast Local) {
@@ -3922,7 +3929,7 @@ fn resolve_pattern(
#[tracing::instrument(skip(self, bindings), level = "debug")]
fn resolve_pattern_inner(
&mut self,
- pat: &Pat,
+ pat: &'ast Pat,
pat_src: PatternSource,
bindings: &mut PatternBindings,
) {
@@ -3982,6 +3989,31 @@ fn resolve_pattern_inner(
// Prevent visiting `ps` as we've already done so above.
return false;
}
+ PatKind::Guard(ref subpat, ref guard) => {
+ // Add a new set of bindings to the stack to collect bindings in `subpat`.
+ bindings.push((PatBoundCtx::Product, Default::default()));
+ // Resolving `subpat` adds bindings onto the newly-pushed context. After, the
+ // total number of contexts on the stack should be the same as before.
+ let binding_ctx_stack_len = bindings.len();
+ self.resolve_pattern_inner(subpat, pat_src, bindings);
+ assert_eq!(bindings.len(), binding_ctx_stack_len);
+ // These bindings, but none from the surrounding pattern, are visible in the
+ // guard; put them in scope and resolve `guard`.
+ let subpat_bindings = bindings.pop().unwrap().1;
+ self.with_rib(ValueNS, RibKind::Normal, |this| {
+ *this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone();
+ this.resolve_expr(guard, None);
+ });
+ // Propagate the subpattern's bindings upwards.
+ // FIXME(guard_patterns): For `if let` guards, we'll also need to get the
+ // bindings introduced by the guard from its rib and propagate them upwards.
+ // This will require checking the identifiers for overlaps with `bindings`, like
+ // what `fresh_binding` does (ideally sharing its logic). To keep them separate
+ // from `subpat_bindings`, we can introduce a fresh rib for the guard.
+ bindings.last_mut().unwrap().1.extend(subpat_bindings);
+ // Prevent visiting `subpat` as we've already done so above.
+ return false;
+ }
_ => {}
}
true
diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs
index 74fb5817081c..095f66eeb906 100644
--- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs
+++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs
@@ -22,7 +22,6 @@ fn other_guards_dont() {
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
@@ -37,7 +36,6 @@ fn other_guards_dont() {
fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
//~^ ERROR: guard patterns are experimental
-//~| ERROR: cannot find value `x`
fn guard(x: T) -> bool {
unimplemented!()
diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
index 8b85b663889f..b0bf302f3cb6 100644
--- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
+++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr
@@ -10,24 +10,6 @@ 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:23: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:23:11
- |
-LL | let ((x if guard(x)) | x) = 0;
- | ^
-
error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:18:15
|
@@ -51,7 +33,7 @@ LL | let ((x if guard(x)) | x) = 0;
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
- --> $DIR/feature-gate-guard-patterns.rs:27:18
+ --> $DIR/feature-gate-guard-patterns.rs:26:18
|
LL | if let (x if guard(x)) = 0 {}
| ^^^^^^^^
@@ -62,7 +44,7 @@ LL | if let (x if guard(x)) = 0 {}
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
- --> $DIR/feature-gate-guard-patterns.rs:30:21
+ --> $DIR/feature-gate-guard-patterns.rs:29:21
|
LL | while let (x if guard(x)) = 0 {}
| ^^^^^^^^
@@ -73,7 +55,7 @@ LL | while let (x if guard(x)) = 0 {}
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
- --> $DIR/feature-gate-guard-patterns.rs:34:21
+ --> $DIR/feature-gate-guard-patterns.rs:33:21
|
LL | while let (x if guard(x)) = 0 {}
| ^^^^^^^^
@@ -84,7 +66,7 @@ LL | while let (x if guard(x)) = 0 {}
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
- --> $DIR/feature-gate-guard-patterns.rs:38:39
+ --> $DIR/feature-gate-guard-patterns.rs:37:39
|
LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
| ^^^^^^^^
@@ -94,7 +76,6 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {
= 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: aborting due to 9 previous errors
+error: aborting due to 7 previous errors
-Some errors have detailed explanations: E0425, E0658.
-For more information about an error, try `rustc --explain E0425`.
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs
new file mode 100644
index 000000000000..83ad8c76bb1c
--- /dev/null
+++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs
@@ -0,0 +1,81 @@
+//! Test that guard patterns can see bindings already in scope and bindings introduced in their
+//! subpattern, but no other bindings from the containing pattern. Also make sure bindings
+//! introduced in guard patterns are visible in fn/arm/loop/etc bodies.
+
+#![feature(guard_patterns)]
+#![expect(incomplete_features)]
+
+fn good_fn_item(((x if x) | x): bool) -> bool { x }
+
+fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {}
+//~^ ERROR cannot find value `x` in this scope
+fn bad_fn_item_2(((x if y) | x): bool, y: bool) {}
+//~^ ERROR cannot find value `y` in this scope
+
+fn main() {
+ let ((local if local) if local) = false;
+
+ match (true, true) {
+ (x if local, y if good_fn_item(y)) => x && y,
+ (x, y if x) => x && y,
+ //~^ ERROR cannot find value `x` in this scope
+ (x if y, y) => x && y,
+ //~^ ERROR cannot find value `y` in this scope
+ };
+
+ match (true,) {
+ (x @ y if x && y,) => x && y,
+ (x @ (y if y),) => x && y,
+ (x @ (y if x),) => x && y,
+ //~^ ERROR cannot find value `x` in this scope
+ };
+
+ match (Ok(true),) {
+ ((Ok(x) | Err(x)) if good_fn_item(x),) => x,
+ ((Ok(x) if local) | (Err(x) if good_fn_item(x)),) => x,
+ ((Ok(x if x) if x) | (Err(x if x) if x) if x,) if x => x,
+ ((Ok(x) if y) | (Err(y) if x),) => x && y,
+ //~^ ERROR variable `x` is not bound in all patterns
+ //~| ERROR variable `y` is not bound in all patterns
+ //~| ERROR cannot find value `x` in this scope
+ //~| ERROR cannot find value `y` in this scope
+ };
+
+ let (_ if nonexistent) = true;
+ //~^ ERROR cannot find value `nonexistent` in this scope
+ if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+ //~^ ERROR cannot find value `x` in this scope
+ //~| ERROR cannot find value `y` in this scope
+ while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+ //~^ ERROR cannot find value `x` in this scope
+ //~| ERROR cannot find value `y` in this scope
+ for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
+ //~^ ERROR cannot find value `x` in this scope
+ //~| ERROR cannot find value `y` in this scope
+
+ (|(x if x), (y if y)| x && y)(true, true);
+ (|(x if y), (y if x)| x && y)(true, true);
+ //~^ ERROR cannot find value `x` in this scope
+ //~| ERROR cannot find value `y` in this scope
+
+ // FIXME(guard_patterns): mismatched bindings are not yet allowed
+ match Some(0) {
+ Some(x if x > 0) | None => {}
+ //~^ ERROR variable `x` is not bound in all patterns
+ }
+}
+
+/// Make sure shadowing is handled properly. In particular, if a pattern shadows an identifier,
+/// a guard pattern's guard should still see the original binding if the shadowing binding isn't in
+/// its subpattern.
+fn test_shadowing(local: bool) -> u8 {
+ match (0, 0) {
+ // The `local` binding here shadows the `bool` definition, so we get a type error.
+ //~v ERROR mismatched types
+ local if local => 0,
+ // The guards here should see the `bool` definition of `local`, not the new `u8` binding.
+ // The body should see the new binding.
+ (local, _ if local) => local,
+ (_ if local, local) => local,
+ }
+}
diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr
new file mode 100644
index 000000000000..d76e60478a14
--- /dev/null
+++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr
@@ -0,0 +1,133 @@
+error[E0408]: variable `y` is not bound in all patterns
+ --> $DIR/name-resolution.rs:37:10
+ |
+LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
+ | ^^^^^^^^^^^^ - variable not in all patterns
+ | |
+ | pattern doesn't bind `y`
+
+error[E0408]: variable `x` is not bound in all patterns
+ --> $DIR/name-resolution.rs:37:25
+ |
+LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
+ | - ^^^^^^^^^^^^^ pattern doesn't bind `x`
+ | |
+ | variable not in all patterns
+
+error[E0408]: variable `x` is not bound in all patterns
+ --> $DIR/name-resolution.rs:63:28
+ |
+LL | Some(x if x > 0) | None => {}
+ | - ^^^^ pattern doesn't bind `x`
+ | |
+ | variable not in all patterns
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:10:34
+ |
+LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {}
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+ --> $DIR/name-resolution.rs:12:25
+ |
+LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {}
+ | ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:20:18
+ |
+LL | (x, y if x) => x && y,
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+ --> $DIR/name-resolution.rs:22:15
+ |
+LL | (x if y, y) => x && y,
+ | ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:29:20
+ |
+LL | (x @ (y if x),) => x && y,
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+ --> $DIR/name-resolution.rs:37:20
+ |
+LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
+ | ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:37:36
+ |
+LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `nonexistent` in this scope
+ --> $DIR/name-resolution.rs:44:15
+ |
+LL | let (_ if nonexistent) = true;
+ | ^^^^^^^^^^^ not found in this scope
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:46:22
+ |
+LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+ --> $DIR/name-resolution.rs:46:33
+ |
+LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+ | ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:49:25
+ |
+LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+ --> $DIR/name-resolution.rs:49:36
+ |
+LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
+ | ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:52:19
+ |
+LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0425]: cannot find value `y` in this scope
+ --> $DIR/name-resolution.rs:52:30
+ |
+LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
+ | ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `y` in this scope
+ --> $DIR/name-resolution.rs:57:13
+ |
+LL | (|(x if y), (y if x)| x && y)(true, true);
+ | ^ help: a local variable with a similar name exists: `x`
+
+error[E0425]: cannot find value `x` in this scope
+ --> $DIR/name-resolution.rs:57:23
+ |
+LL | (|(x if y), (y if x)| x && y)(true, true);
+ | ^ help: a local variable with a similar name exists: `y`
+
+error[E0308]: mismatched types
+ --> $DIR/name-resolution.rs:75:18
+ |
+LL | local if local => 0,
+ | ^^^^^ expected `bool`, found `({integer}, {integer})`
+ |
+ = note: expected type `bool`
+ found tuple `({integer}, {integer})`
+
+error: aborting due to 20 previous errors
+
+Some errors have detailed explanations: E0308, E0408, E0425.
+For more information about an error, try `rustc --explain E0308`.