diff options
| author | dianne <diannes.gm@gmail.com> | 2025-05-07 06:59:30 -0700 |
|---|---|---|
| committer | dianne <diannes.gm@gmail.com> | 2025-05-18 04:21:57 -0700 |
| commit | f0b8ec1d71f055cbdb741565eaddabc93bf1ae75 (patch) | |
| tree | b5a4b808c917d313c1533cd92f72a2f9ac589e7d | |
| parent | 30a0ac66dbb15ee8dd3951499b34df48e1d758a0 (diff) | |
| download | rust-f0b8ec1d71f055cbdb741565eaddabc93bf1ae75.tar.gz rust-f0b8ec1d71f055cbdb741565eaddabc93bf1ae75.zip | |
name resolution for guard patterns
| -rw-r--r-- | compiler/rustc_ast/src/ast.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 36 | ||||
| -rw-r--r-- | tests/ui/feature-gates/feature-gate-guard-patterns.rs | 2 | ||||
| -rw-r--r-- | tests/ui/feature-gates/feature-gate-guard-patterns.stderr | 31 | ||||
| -rw-r--r-- | tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs | 81 | ||||
| -rw-r--r-- | tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr | 133 |
6 files changed, 255 insertions, 30 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 114b9835b98..ab48a2899a7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -611,7 +611,7 @@ impl Pat { /// 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 aa211a8f3c2..1b682d0cf8a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -799,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r 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 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { #[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 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // 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 74fb5817081..095f66eeb90 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<T>(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 8b85b663889..b0bf302f3cb 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 00000000000..83ad8c76bb1 --- /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 00000000000..d76e60478a1 --- /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`. |
