From c343b2a47c29bdfe4c611ba74f9c9e455f12539b Mon Sep 17 00:00:00 2001 From: dianne Date: Mon, 19 May 2025 23:18:08 -0700 Subject: [PATCH] `gather_locals`: only visit guard pattern guards when checking the guard When checking a pattern with guards in it, `GatherLocalsVisitor` will visit both the pattern (when type-checking the let, arm, or param containing it) and the guard expression (when checking the guard itself). This keeps it from visiting the guard when visiting the pattern, since otherwise it would gather locals from the guard twice, which would lead to a delayed bug: "evaluated expression more than once". --- compiler/rustc_hir_typeck/src/gather_locals.rs | 7 ++++++- .../only-gather-locals-once.rs | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 956671fc66ed2..7d99b0e78694e 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -218,7 +218,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { ); } let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take(); - intravisit::walk_pat(self, p); + if let PatKind::Guard(subpat, _) = p.kind { + // We'll visit the guard when checking it. Don't gather its locals twice. + self.visit_pat(subpat); + } else { + intravisit::walk_pat(self, p); + } self.outermost_fn_param_pat = old_outermost_fn_param_pat; } diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs new file mode 100644 index 0000000000000..7bb39ca7bb987 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/only-gather-locals-once.rs @@ -0,0 +1,15 @@ +//@ check-pass +//! Test that `GatherLocalsVisitor` only visits expressions in guard patterns when checking the +//! expressions, and not a second time when visiting the pattern. If locals are declared inside the +//! the guard expression, it would ICE if visited twice ("evaluated expression more than once"). + +#![feature(guard_patterns)] +#![expect(incomplete_features)] + +fn main() { + match (0,) { + // FIXME(guard_patterns): liveness lints don't work yet; this will ICE without the `_`. + (_ if { let _x = false; _x },) => {} + _ => {} + } +}