name resolution for guard patterns

This commit is contained in:
dianne
2025-05-07 06:59:30 -07:00
parent 30a0ac66db
commit f0b8ec1d71
6 changed files with 255 additions and 30 deletions
+1 -1
View File
@@ -611,7 +611,7 @@ pub fn to_ty(&self) -> Option<P<Ty>> {
/// 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;
}
+34 -2
View File
@@ -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
@@ -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!()
@@ -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`.
@@ -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,
}
}
@@ -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`.