From 9744d19f541b2fd81a91d4077ec595a633342ddb Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 29 Mar 2026 10:22:16 +0500 Subject: [PATCH 01/32] propagate guard patterns into `MatchPairTree` --- compiler/rustc_mir_build/src/builder/matches/match_pair.rs | 7 ++++--- compiler/rustc_mir_build/src/builder/matches/mod.rs | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index bd18a215aea70..ad20d71c8db60 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -361,9 +361,10 @@ impl<'tcx> MatchPairTree<'tcx> { Some(TestableCase::Deref { temp, mutability }) } - PatKind::Guard { .. } => { - // FIXME(guard_patterns) - None + PatKind::Guard { ref subpattern, condition } => { + extra_data.guard_paterns.push(condition); + MatchPairTree::for_pattern(place_builder, subpattern, cx, match_pairs, extra_data); + return; } PatKind::Never => Some(TestableCase::Never), diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 5604e86e06722..a6518c52d17db 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -966,6 +966,9 @@ struct PatternExtraData<'tcx> { /// Whether this corresponds to a never pattern. is_never: bool, + + /// [`ExprId`]s of subpattern conditions + guard_paterns: Vec } impl<'tcx> PatternExtraData<'tcx> { @@ -1010,6 +1013,7 @@ impl<'tcx> FlatPat<'tcx> { bindings: Vec::new(), ascriptions: Vec::new(), is_never: pattern.is_never_pattern(), + guard_paterns: Vec::new() }; MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); @@ -1420,6 +1424,8 @@ struct MatchTreeSubBranch<'tcx> { bindings: Vec>, /// The ascriptions to set up in this sub-branch. ascriptions: Vec>, + /// The guard patterns present in this sub-branch + guard_patterns: Vec, /// Whether the sub-branch corresponds to a never pattern. is_never: bool, } @@ -1471,6 +1477,7 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { .cloned() .chain(candidate.extra_data.ascriptions) .collect(), + guard_patterns: candidate.extra_data.guard_paterns, is_never: candidate.extra_data.is_never, } } From eaed72872f0a02f38d390543ed384070e4574bc3 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 29 Mar 2026 17:49:11 +0500 Subject: [PATCH 02/32] Lower guard patterns to MIR --- .../src/builder/matches/mod.rs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index a6518c52d17db..a9622bd3f816a 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2434,9 +2434,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Lower an instance of the arm guard (if present) for this candidate, // and then perform bindings for the arm body. if let Some((arm, match_scope)) = arm_match_scope - && let Some(guard) = arm.guard + && (arm.guard.is_some() || !sub_branch.guard_patterns.is_empty()) { let tcx = self.tcx; + + let mut guards = sub_branch.guard_patterns; + if let Some(guard) = arm.guard { + guards.push(guard); + }; // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. @@ -2463,14 +2468,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let (post_guard_block, otherwise_post_guard_block) = self.in_if_then_scope(match_scope, guard_span, |this| { - guard_span = this.thir[guard].span; - this.then_else_break( - block, - guard, - None, // Use `self.local_scope()` as the temp scope - this.source_info(arm.span), - DeclareLetBindings::No, // For guards, `let` bindings are declared separately - ) + guards.into_iter().fold(BlockAnd(block, ()), |block, guard| { + guard_span = this.thir[guard].span; + this.then_else_break( + block.0, + guard, + None, // Use `self.local_scope()` as the temp scope + this.source_info(arm.span), + DeclareLetBindings::No, // For guards, `let` bindings are declared separately + ) + }) }); // If this isn't the final sub-branch being lowered, we need to unschedule drops of From 3b5b2eb907555eeaf78c42d6680fdc0806a14694 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 29 Mar 2026 17:53:34 +0500 Subject: [PATCH 03/32] bless guard pattern testing + fmt --- .../src/builder/matches/match_pair.rs | 2 +- .../src/builder/matches/mod.rs | 10 ++-- .../name-resolution.rs | 1 - .../name-resolution.stderr | 53 ++++++++----------- 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index ad20d71c8db60..612d76d5955fe 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -362,7 +362,7 @@ impl<'tcx> MatchPairTree<'tcx> { } PatKind::Guard { ref subpattern, condition } => { - extra_data.guard_paterns.push(condition); + extra_data.guard_patterns.push(condition); MatchPairTree::for_pattern(place_builder, subpattern, cx, match_pairs, extra_data); return; } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index a9622bd3f816a..25ef6a10b9dd3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -966,9 +966,9 @@ struct PatternExtraData<'tcx> { /// Whether this corresponds to a never pattern. is_never: bool, - + /// [`ExprId`]s of subpattern conditions - guard_paterns: Vec + guard_patterns: Vec, } impl<'tcx> PatternExtraData<'tcx> { @@ -1013,7 +1013,7 @@ impl<'tcx> FlatPat<'tcx> { bindings: Vec::new(), ascriptions: Vec::new(), is_never: pattern.is_never_pattern(), - guard_paterns: Vec::new() + guard_patterns: Vec::new(), }; MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); @@ -1477,7 +1477,7 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { .cloned() .chain(candidate.extra_data.ascriptions) .collect(), - guard_patterns: candidate.extra_data.guard_paterns, + guard_patterns: candidate.extra_data.guard_patterns, is_never: candidate.extra_data.is_never, } } @@ -2437,7 +2437,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { && (arm.guard.is_some() || !sub_branch.guard_patterns.is_empty()) { let tcx = self.tcx; - + let mut guards = sub_branch.guard_patterns; if let Some(guard) = arm.guard { guards.push(guard); diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs index 21edb9ceeff48..83ad8c76bb1cf 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs @@ -6,7 +6,6 @@ #![expect(incomplete_features)] fn good_fn_item(((x if x) | x): bool) -> bool { x } -//~^ ERROR: used binding `x` is possibly-uninitialized [E0381] fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} //~^ ERROR cannot find value `x` in this scope diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr index a95ffdd42532f..44e42f1427074 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `y` is not bound in all patterns - --> $DIR/name-resolution.rs:38:10 + --> $DIR/name-resolution.rs:37:10 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^^^^^^^^^^^^ - variable not in all patterns @@ -13,7 +13,7 @@ LL + ((Ok(x) if y) | (Err(x) if x),) => x && y, | error[E0408]: variable `x` is not bound in all patterns - --> $DIR/name-resolution.rs:38:25 + --> $DIR/name-resolution.rs:37:25 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | - ^^^^^^^^^^^^^ pattern doesn't bind `x` @@ -27,7 +27,7 @@ LL + ((Ok(y) if y) | (Err(y) if x),) => x && y, | error[E0408]: variable `x` is not bound in all patterns - --> $DIR/name-resolution.rs:64:28 + --> $DIR/name-resolution.rs:63:28 | LL | Some(x if x > 0) | None => {} | - ^^^^ pattern doesn't bind `x` @@ -35,7 +35,7 @@ LL | Some(x if x > 0) | None => {} | variable not in all patterns error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:11:34 + --> $DIR/name-resolution.rs:10:34 | LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} | ^ @@ -47,7 +47,7 @@ LL + fn bad_fn_item_1(x: bool, ((y if y) | y): bool) {} | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:13:25 + --> $DIR/name-resolution.rs:12:25 | LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} | ^ @@ -59,7 +59,7 @@ LL + fn bad_fn_item_2(((x if x) | x): bool, y: bool) {} | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:21:18 + --> $DIR/name-resolution.rs:20:18 | LL | (x, y if x) => x && y, | ^ @@ -71,7 +71,7 @@ LL + (x, y if y) => x && y, | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:23:15 + --> $DIR/name-resolution.rs:22:15 | LL | (x if y, y) => x && y, | ^ @@ -83,7 +83,7 @@ LL + (x if x, y) => x && y, | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:30:20 + --> $DIR/name-resolution.rs:29:20 | LL | (x @ (y if x),) => x && y, | ^ @@ -95,7 +95,7 @@ LL + (x @ (y if y),) => x && y, | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:38:20 + --> $DIR/name-resolution.rs:37:20 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^ @@ -107,7 +107,7 @@ LL + ((Ok(x) if x) | (Err(y) if x),) => x && y, | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:38:36 + --> $DIR/name-resolution.rs:37:36 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^ @@ -119,13 +119,13 @@ LL + ((Ok(x) if y) | (Err(y) if y),) => x && y, | error[E0425]: cannot find value `nonexistent` in this scope - --> $DIR/name-resolution.rs:45:15 + --> $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:47:22 + --> $DIR/name-resolution.rs:46:22 | LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -137,7 +137,7 @@ LL + if let ((x, y if y) | (x if y, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:47:33 + --> $DIR/name-resolution.rs:46:33 | LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -149,7 +149,7 @@ LL + if let ((x, y if x) | (x if x, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:50:25 + --> $DIR/name-resolution.rs:49:25 | LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -161,7 +161,7 @@ LL + while let ((x, y if y) | (x if y, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:50:36 + --> $DIR/name-resolution.rs:49:36 | LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -173,7 +173,7 @@ LL + while let ((x, y if x) | (x if x, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:53:19 + --> $DIR/name-resolution.rs:52:19 | LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } | ^ @@ -185,7 +185,7 @@ LL + for ((x, y if y) | (x if y, y)) in [(true, true)] { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:53:30 + --> $DIR/name-resolution.rs:52:30 | LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } | ^ @@ -197,7 +197,7 @@ LL + for ((x, y if x) | (x if x, y)) in [(true, true)] { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:58:13 + --> $DIR/name-resolution.rs:57:13 | LL | (|(x if y), (y if x)| x && y)(true, true); | ^ @@ -209,7 +209,7 @@ LL + (|(x if x), (y if x)| x && y)(true, true); | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:58:23 + --> $DIR/name-resolution.rs:57:23 | LL | (|(x if y), (y if x)| x && y)(true, true); | ^ @@ -221,7 +221,7 @@ LL + (|(x if y), (y if y)| x && y)(true, true); | error[E0308]: mismatched types - --> $DIR/name-resolution.rs:76:18 + --> $DIR/name-resolution.rs:75:18 | LL | local if local => 0, | ^^^^^ expected `bool`, found `({integer}, {integer})` @@ -229,16 +229,7 @@ LL | local if local => 0, = note: expected type `bool` found tuple `({integer}, {integer})` -error[E0381]: used binding `x` is possibly-uninitialized - --> $DIR/name-resolution.rs:8:49 - | -LL | fn good_fn_item(((x if x) | x): bool) -> bool { x } - | - - ^ `x` used here but it is possibly-uninitialized - | | | - | | binding initialized here in some conditions - | binding declared here but left uninitialized - -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors -Some errors have detailed explanations: E0308, E0381, E0408, E0425. +Some errors have detailed explanations: E0308, E0408, E0425. For more information about an error, try `rustc --explain E0308`. From 22563b8e898024a4b1ea0e9d3c8b2ffb922d99e2 Mon Sep 17 00:00:00 2001 From: human9000 Date: Mon, 30 Mar 2026 16:39:37 +0500 Subject: [PATCH 04/32] add mir correctness test --- .../rfc-3637-guard-patterns/correct-mir.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs b/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs new file mode 100644 index 0000000000000..61518899cb0ac --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs @@ -0,0 +1,20 @@ +//! Check MIR correctness for general guard patterns usage + +//@ compile-flags: -Zvalidate-mir -Zlint-mir +//@ run-pass + +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +fn main() { + generic_usage(true, false, true); +} + +fn generic_usage(x: bool, y: bool, z: bool) -> bool { + match (x, y) { + (true if z, false if !z) => true, + (false if z, true if z) => false, + (true, true) => true, + (false, false) => false + } +} From 6fd9b82b326374b8a9e8995c02db106fbef26335 Mon Sep 17 00:00:00 2001 From: human9000 Date: Mon, 30 Mar 2026 16:41:56 +0500 Subject: [PATCH 05/32] set `has_guard` to `true` if guard patterns are present Co-authored-by: Dianne --- compiler/rustc_hir_typeck/src/expr_use_visitor.rs | 2 +- compiler/rustc_mir_build/src/builder/matches/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index f3d0b4d000c28..a4ea1568546d1 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -947,10 +947,10 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx read_discriminant(); } } + PatKind::Guard(..) => read_discriminant(), PatKind::Or(_) | PatKind::Box(_) | PatKind::Ref(..) - | PatKind::Guard(..) | PatKind::Tuple(..) | PatKind::Wild | PatKind::Missing diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 25ef6a10b9dd3..bbc43c22be329 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1114,10 +1114,10 @@ impl<'tcx> Candidate<'tcx> { ) -> Self { // Use `FlatPat` to build simplified match pairs, then immediately // incorporate them into a new candidate. - Self::from_flat_pat( - FlatPat::new(place, pattern, cx), - matches!(has_guard, HasMatchGuard::Yes), - ) + let flat_pat = FlatPat::new(place, pattern, cx); + let has_guard = matches!(has_guard, HasMatchGuard::Yes) + || !flat_pat.extra_data.guard_patterns.is_empty(); + Self::from_flat_pat(flat_pat, has_guard) } /// Incorporates an already-simplified [`FlatPat`] into a new candidate. From 36173926a127bf8c2cd429b05d26e74324104e18 Mon Sep 17 00:00:00 2001 From: human9000 Date: Thu, 2 Apr 2026 22:12:30 +0500 Subject: [PATCH 06/32] update MIR correctness test the problem was in unimplemented exhaustiveness checking, which caused SIGILL --- tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs b/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs index 61518899cb0ac..cf7f824eb0d24 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs +++ b/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs @@ -7,12 +7,12 @@ #![allow(incomplete_features)] fn main() { - generic_usage(true, false, true); + assert!(generic_usage(true, false, true)); } fn generic_usage(x: bool, y: bool, z: bool) -> bool { match (x, y) { - (true if z, false if !z) => true, + (true if z, false if z) => true, (false if z, true if z) => false, (true, true) => true, (false, false) => false From d6f6226ef6b423ad9c09dab090ca473680af6b37 Mon Sep 17 00:00:00 2001 From: human9000 Date: Fri, 3 Apr 2026 10:42:04 +0500 Subject: [PATCH 07/32] propagate `Scope` needed for guard patterns down to MIR Co-authored-by: dianne --- compiler/rustc_middle/src/thir.rs | 3 +++ .../src/builder/matches/match_pair.rs | 6 +++++ .../src/builder/matches/mod.rs | 9 ++++++- .../rustc_mir_build/src/thir/pattern/mod.rs | 26 ++++++++++++++----- compiler/rustc_mir_build/src/thir/print.rs | 2 +- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 51f33691bb7dd..8c354737ceeda 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -646,6 +646,9 @@ pub struct PatExtra<'tcx> { /// User-written types that must be preserved into MIR so that they can be /// checked. pub ascriptions: Vec>, + + #[type_visitable(ignore)] + pub scope: Option, } #[derive(Clone, Debug, HashStable, TypeVisitable)] diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 612d76d5955fe..32531707c9d68 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -362,6 +362,12 @@ impl<'tcx> MatchPairTree<'tcx> { } PatKind::Guard { ref subpattern, condition } => { + if extra_data.scope.is_none() + && let Some(ref extra) = pattern.extra + { + extra_data.scope = extra.scope + } + extra_data.guard_patterns.push(condition); MatchPairTree::for_pattern(place_builder, subpattern, cx, match_pairs, extra_data); return; diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index bbc43c22be329..85c49e4232694 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -14,7 +14,7 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node}; -use rustc_middle::middle::region::{self, TempLifetime}; +use rustc_middle::middle::region::{self, Scope, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; @@ -969,6 +969,9 @@ struct PatternExtraData<'tcx> { /// [`ExprId`]s of subpattern conditions guard_patterns: Vec, + + /// Scope of this sub-branch + scope: Option, } impl<'tcx> PatternExtraData<'tcx> { @@ -1014,6 +1017,7 @@ impl<'tcx> FlatPat<'tcx> { ascriptions: Vec::new(), is_never: pattern.is_never_pattern(), guard_patterns: Vec::new(), + scope: None, }; MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); @@ -1428,6 +1432,8 @@ struct MatchTreeSubBranch<'tcx> { guard_patterns: Vec, /// Whether the sub-branch corresponds to a never pattern. is_never: bool, + /// Scope of this sub-branch; used mostly by guard patterns + scope: Option, } /// A branch in the output of match lowering. @@ -1479,6 +1485,7 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { .collect(), guard_patterns: candidate.extra_data.guard_patterns, is_never: candidate.extra_data.is_never, + scope: candidate.extra_data.scope, } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 7d4e25cd814b9..f84f098544658 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -15,8 +15,10 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, RangeEnd}; use rustc_index::Idx; +use rustc_middle::middle::region; use rustc_middle::thir::{ - Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, + Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatExtra, PatKind, PatRange, + PatRangeBoundary, }; use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment}; use rustc_middle::ty::layout::IntegerExt; @@ -319,6 +321,8 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let ty = self.typeck_results.node_type(pat.hir_id); let span = pat.span; + let mut extra = None; + // Some of these match arms return a `Box` early, while others // evaluate to a `PatKind` that will become a `Box` at the end of // this function. @@ -447,17 +451,27 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, - hir::PatKind::Guard(pat, condition) => PatKind::Guard { - subpattern: self.lower_pattern(pat), - condition: self.upper.mirror_expr(condition), - }, + hir::PatKind::Guard(pat, condition) => { + // In some cases during MIR lowering we need a Scope + let scope = region::Scope { + local_id: pat.hir_id.local_id, + data: region::ScopeData::MatchGuard, + }; + + extra = Some(Box::new(PatExtra { scope: Some(scope), ..Default::default() })); + + PatKind::Guard { + subpattern: self.lower_pattern(pat), + condition: self.upper.mirror_expr(condition), + } + } hir::PatKind::Err(guar) => PatKind::Error(guar), }; // For pattern kinds that haven't already returned, create a `thir::Pat` // with the HIR pattern node's type and span. - Box::new(Pat { span, ty, kind, extra: None }) + Box::new(Pat { span, ty, kind, extra }) } fn lower_tuple_subpats( diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index ea34e5f4d97df..387540b7133de 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -695,7 +695,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { return; }; - let PatExtra { expanded_const, ascriptions } = extra; + let PatExtra { expanded_const, ascriptions, .. } = extra; print_indented!(self, "extra: PatExtra {", depth_lvl); print_indented!(self, format_args!("expanded_const: {expanded_const:?}"), depth_lvl + 1); From 76839448bb3301c595a8c7fb0749df8434d45445 Mon Sep 17 00:00:00 2001 From: human9000 Date: Fri, 3 Apr 2026 10:43:06 +0500 Subject: [PATCH 08/32] guard matched candidate in case guard patterns are present, but `arm_match_scope` isn't provided Co-authored-by: dianne --- .../src/builder/matches/mod.rs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 85c49e4232694..cda660dc3ca7d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2438,17 +2438,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.ascribe_types(block, sub_branch.ascriptions); - // Lower an instance of the arm guard (if present) for this candidate, - // and then perform bindings for the arm body. - if let Some((arm, match_scope)) = arm_match_scope - && (arm.guard.is_some() || !sub_branch.guard_patterns.is_empty()) + if !sub_branch.guard_patterns.is_empty() + || arm_match_scope.is_some_and(|(arm, _)| arm.guard.is_some()) { let tcx = self.tcx; let mut guards = sub_branch.guard_patterns; - if let Some(guard) = arm.guard { - guards.push(guard); - }; + let (arm_span, arm_scope, match_scope) = + if let Some((arm, match_scope)) = arm_match_scope { + if let Some(arm_guard) = arm.guard { + guards.push(arm_guard); + }; + (arm.span, arm.scope, match_scope) + } else { + let span = sub_branch.span; + // There must be a scope if a guard pattern is present + let scope = sub_branch.scope.unwrap(); + + (span, scope, scope) + }; // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. @@ -2481,7 +2489,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.0, guard, None, // Use `self.local_scope()` as the temp scope - this.source_info(arm.span), + this.source_info(arm_span), DeclareLetBindings::No, // For guards, `let` bindings are declared separately ) }) @@ -2491,7 +2499,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // bindings and temporaries created for and by the guard. As a result, the drop order // for the arm will correspond to the binding order of the final sub-branch lowered. if matches!(schedule_drops, ScheduleDrops::No) { - self.clear_match_arm_and_guard_scopes(arm.scope); + self.clear_match_arm_and_guard_scopes(arm_scope); } let source_info = self.source_info(guard_span); From b094f0d40b3cd67a8313023c698697f2964751cc Mon Sep 17 00:00:00 2001 From: human9000 Date: Fri, 3 Apr 2026 10:51:46 +0500 Subject: [PATCH 09/32] Don't mutate `guard_span` during MIR construction Co-authored-by: dianne --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index cda660dc3ca7d..7f557f88cc5f3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2446,10 +2446,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut guards = sub_branch.guard_patterns; let (arm_span, arm_scope, match_scope) = if let Some((arm, match_scope)) = arm_match_scope { + let mut span = arm.span; if let Some(arm_guard) = arm.guard { + span = span.to(self.thir[arm_guard].span); guards.push(arm_guard); }; - (arm.span, arm.scope, match_scope) + (span, arm.scope, match_scope) } else { let span = sub_branch.span; // There must be a scope if a guard pattern is present @@ -2479,12 +2481,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); } - let mut guard_span = rustc_span::DUMMY_SP; + // If we are in this branch, there are definitely some guards here + let guard_span = self.thir[*guards.first().unwrap()] + .span + .to(self.thir[*guards.last().unwrap()].span); let (post_guard_block, otherwise_post_guard_block) = self.in_if_then_scope(match_scope, guard_span, |this| { guards.into_iter().fold(BlockAnd(block, ()), |block, guard| { - guard_span = this.thir[guard].span; this.then_else_break( block.0, guard, From b2c991e2a6ef1f6b8b4da76afcc5d2a47853c6ca Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 4 Apr 2026 20:48:31 +0500 Subject: [PATCH 10/32] Don't read disrciminant for guard patterns (for now) --- compiler/rustc_hir_typeck/src/expr_use_visitor.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index a4ea1568546d1..ab0c000a056c8 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -947,13 +947,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx read_discriminant(); } } - PatKind::Guard(..) => read_discriminant(), + PatKind::Or(_) | PatKind::Box(_) | PatKind::Ref(..) | PatKind::Tuple(..) | PatKind::Wild | PatKind::Missing + | PatKind::Guard(..) | PatKind::Err(_) => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. From 6e62fe175b4d2c3d08f8c86fa870d5ddc18b322e Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 5 Apr 2026 10:30:03 +0500 Subject: [PATCH 11/32] Properly span guard patterns --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 7f557f88cc5f3..cf61f05b41c7b 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2481,13 +2481,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); } - // If we are in this branch, there are definitely some guards here - let guard_span = self.thir[*guards.first().unwrap()] - .span - .to(self.thir[*guards.last().unwrap()].span); - let (post_guard_block, otherwise_post_guard_block) = - self.in_if_then_scope(match_scope, guard_span, |this| { + self.in_if_then_scope(match_scope, arm_span, |this| { guards.into_iter().fold(BlockAnd(block, ()), |block, guard| { this.then_else_break( block.0, @@ -2506,8 +2501,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.clear_match_arm_and_guard_scopes(arm_scope); } - let source_info = self.source_info(guard_span); - let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span)); + let source_info = self.source_info(arm_span); + let guard_end = self.source_info(tcx.sess.source_map().end_point(arm_span)); let guard_frame = self.guard_context.pop().unwrap(); debug!("Exiting guard building context with locals: {:?}", guard_frame); From 9dfaa884699dc4d99ad5cec8de4d955279233051 Mon Sep 17 00:00:00 2001 From: human9000 Date: Tue, 7 Apr 2026 15:55:11 +0500 Subject: [PATCH 12/32] Abstract away match arms and guard patterns handling Co-authored-by: dianne --- .../src/builder/matches/mod.rs | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index cf61f05b41c7b..1f78d9500474f 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2418,7 +2418,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// moving the binding once the guard has evaluated to true (see below). fn bind_and_guard_matched_candidate( &mut self, - sub_branch: MatchTreeSubBranch<'tcx>, + mut sub_branch: MatchTreeSubBranch<'tcx>, fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, @@ -2436,7 +2436,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return self.cfg.start_new_block(); } - self.ascribe_types(block, sub_branch.ascriptions); + self.ascribe_types(block, mem::take(&mut sub_branch.ascriptions)); if !sub_branch.guard_patterns.is_empty() || arm_match_scope.is_some_and(|(arm, _)| arm.guard.is_some()) @@ -2445,20 +2445,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut guards = sub_branch.guard_patterns; let (arm_span, arm_scope, match_scope) = - if let Some((arm, match_scope)) = arm_match_scope { - let mut span = arm.span; - if let Some(arm_guard) = arm.guard { - span = span.to(self.thir[arm_guard].span); - guards.push(arm_guard); - }; - (span, arm.scope, match_scope) - } else { - let span = sub_branch.span; - // There must be a scope if a guard pattern is present - let scope = sub_branch.scope.unwrap(); - - (span, scope, scope) - }; + self.extract_span_scope(&mut sub_branch, arm_match_scope); + let guards = sub_branch.guard_patterns; // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. @@ -2571,6 +2559,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + fn extract_span_scope( + &mut self, + sub_branch: &mut MatchTreeSubBranch<'tcx>, + arm_match_scope: Option<(&Arm<'tcx>, Scope)>, + ) -> (Span, Scope, Scope) { + let (arm_span, arm_scope, match_scope) = if let Some((arm, match_scope)) = arm_match_scope { + let mut span = arm.span; + if let Some(arm_guard) = arm.guard { + span = span.to(self.thir[arm_guard].span); + sub_branch.guard_patterns.push(arm_guard); + }; + (span, arm.scope, match_scope) + } else { + let span = sub_branch.span; + // There must be a scope if a guard pattern is present + let scope = sub_branch.scope.unwrap(); + + (span, scope, scope) + }; + (arm_span, arm_scope, match_scope) + } + /// Append `AscribeUserType` statements onto the end of `block` /// for each ascription fn ascribe_types( From 9e5af3e600bc0ab33b8add4d7b8c552a3e843064 Mon Sep 17 00:00:00 2001 From: human9000 Date: Tue, 7 Apr 2026 16:11:47 +0500 Subject: [PATCH 13/32] Generalize `sub_branch_bindings` for also guard patterns Co-authored-by: dianne --- .../src/builder/matches/match_pair.rs | 9 +- .../src/builder/matches/mod.rs | 105 ++++++++++-------- .../src/builder/matches/util.rs | 4 +- 3 files changed, 67 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 32531707c9d68..e3ac972c627fa 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -146,7 +146,10 @@ impl<'tcx> MatchPairTree<'tcx> { // or-patterns that will be simplified by `merge_trivial_subcandidates`. In // other words, we can assume this expands into subcandidates. // FIXME(@dianne): this needs updating/removing if we always merge or-patterns - extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); + extra_data.bindings.push(super::OrderedPatternData::FromOrPattern); + } + if !pats[0].extra_data.guard_patterns.is_empty() { + extra_data.guard_patterns.push(super::OrderedPatternData::FromOrPattern); } Some(TestableCase::Or { pats }) } @@ -217,7 +220,7 @@ impl<'tcx> MatchPairTree<'tcx> { // Then push this binding, after any bindings in the subpattern. if let Some(source) = place { - extra_data.bindings.push(super::SubpatternBindings::One(super::Binding { + extra_data.bindings.push(super::OrderedPatternData::One(super::Binding { span: pattern.span, source, var_id: var, @@ -368,8 +371,8 @@ impl<'tcx> MatchPairTree<'tcx> { extra_data.scope = extra.scope } - extra_data.guard_patterns.push(condition); MatchPairTree::for_pattern(place_builder, subpattern, cx, match_pairs, extra_data); + extra_data.guard_patterns.push(super::OrderedPatternData::One(condition)); return; } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 1f78d9500474f..ac8198c16b5f1 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -959,7 +959,7 @@ struct PatternExtraData<'tcx> { span: Span, /// Bindings that must be established. - bindings: Vec>, + bindings: Vec>>, /// Types that must be asserted. ascriptions: Vec>, @@ -968,7 +968,7 @@ struct PatternExtraData<'tcx> { is_never: bool, /// [`ExprId`]s of subpattern conditions - guard_patterns: Vec, + guard_patterns: Vec>, /// Scope of this sub-branch scope: Option, @@ -980,12 +980,16 @@ impl<'tcx> PatternExtraData<'tcx> { } } +/// This is used for patterns where order-preserving behavior related with `|` matters +/// such as bindings or guard patterns +/// +/// See [`sub_branch_ordered_pat_data`] #[derive(Debug, Clone)] -enum SubpatternBindings<'tcx> { - /// A single binding. - One(Binding<'tcx>), - /// Holds the place for an or-pattern's bindings. This ensures their drops are scheduled in the - /// order the primary bindings appear. See rust-lang/rust#142163 for more information. +enum OrderedPatternData { + /// A single guard pat/binding. + One(T), + /// Holds the place for an or-pattern's guard pat/binding. This ensures their drops are scheduled in the + /// order those items appear. See rust-lang/rust#142163 for more information. FromOrPattern, } @@ -1476,14 +1480,20 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { span: candidate.extra_data.span, success_block: candidate.pre_binding_block.unwrap(), otherwise_block: candidate.otherwise_block.unwrap(), - bindings: sub_branch_bindings(parent_data, &candidate.extra_data.bindings), + bindings: sub_branch_ordered_pat_data( + parent_data.iter().map(|parent| parent.bindings.as_slice()), + &candidate.extra_data.bindings, + ), ascriptions: parent_data .iter() .flat_map(|d| &d.ascriptions) .cloned() .chain(candidate.extra_data.ascriptions) .collect(), - guard_patterns: candidate.extra_data.guard_patterns, + guard_patterns: sub_branch_ordered_pat_data( + parent_data.iter().map(|parent| parent.guard_patterns.as_slice()), + &candidate.extra_data.guard_patterns, + ), is_never: candidate.extra_data.is_never, scope: candidate.extra_data.scope, } @@ -1511,61 +1521,65 @@ impl<'tcx> MatchTreeBranch<'tcx> { } } -/// Collects the bindings for a [`MatchTreeSubBranch`], preserving the order they appear in the +/// Collects the bindings/guard patterns for a [`MatchTreeSubBranch`], preserving the order they appear in the /// pattern, as though the or-alternatives chosen in this sub-branch were inlined. -fn sub_branch_bindings<'tcx>( - parents: &[PatternExtraData<'tcx>], - leaf_bindings: &[SubpatternBindings<'tcx>], -) -> Vec> { - // In the common case, all bindings will be in leaves. Allocate to fit the leaf's bindings. - let mut all_bindings = Vec::with_capacity(leaf_bindings.len()); - let mut remainder = parents - .iter() - .map(|parent| parent.bindings.as_slice()) - .chain([leaf_bindings]) - // Skip over unsimplified or-patterns without bindings. - .filter(|bindings| !bindings.is_empty()); - if let Some(candidate_bindings) = remainder.next() { - push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder); +/// +/// *Note: this was introduced in [#143764](https://github.com/rust-lang/rust/pull/143764) to be used for bindings, +/// but has been generalized later to also be utilized for guard patterns* +fn sub_branch_ordered_pat_data<'a, T: Copy>( + remainder: impl Iterator]>, + leaf_items: &'a [OrderedPatternData], +) -> Vec { + // In the common case, all bindings/guard patterns patterns will be in leaves. Allocate to fit the leaf's items. + let mut all_items = Vec::with_capacity(leaf_items.len()); + let mut remainder = remainder + .chain([leaf_items]) + // Skip over unsimplified or-patterns without bindings/guard patterns. + .filter(|item| !item.is_empty()); + if let Some(candidate_item) = remainder.next() { + push_sub_branch_ordered_pat_data(&mut all_items, candidate_item, &mut remainder); } - // Make sure we've included all bindings. For ill-formed patterns like `(x, _ | y)`, we may not - // have collected all bindings yet, since we only check the first alternative when determining - // whether to inline subcandidates' bindings. + // Make sure we've included all bindings/guard patterns. For ill-formed patterns like `(x, _ | y)`, we may not + // have collected all bindings/guard patterns yet, since we only check the first alternative when determining + // whether to inline subcandidates' bindings/guard patterns. // FIXME(@dianne): prevent ill-formed patterns from getting here - while let Some(candidate_bindings) = remainder.next() { + while let Some(candidate_items) = remainder.next() { ty::tls::with(|tcx| { - tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted") + tcx.dcx() + .delayed_bug("mismatched or-pattern bindings/guard patterns but no error emitted") }); // To recover, we collect the rest in an arbitrary order. - push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder); + push_sub_branch_ordered_pat_data(&mut all_items, candidate_items, &mut remainder); } - all_bindings + all_items } -/// Helper for [`sub_branch_bindings`]. Collects bindings from `candidate_bindings` into -/// `flattened`. Bindings in or-patterns are collected recursively from `remainder`. -fn push_sub_branch_bindings<'c, 'tcx: 'c>( - flattened: &mut Vec>, - candidate_bindings: &'c [SubpatternBindings<'tcx>], - remainder: &mut impl Iterator]>, +/// Helper for [`sub_branch_ordered_data`]. Collects bindings/guard patterns from `candidate_items` into +/// `flattened`. Those items in or-patterns are collected recursively from `remainder`. +fn push_sub_branch_ordered_pat_data<'c, T: Copy>( + flattened: &mut Vec, + candidate_items: &'c [OrderedPatternData], + remainder: &mut impl Iterator]>, ) { - for subpat_bindings in candidate_bindings { - match subpat_bindings { - SubpatternBindings::One(binding) => flattened.push(*binding), - SubpatternBindings::FromOrPattern => { - // Inline bindings from an or-pattern. By construction, this always + for subpat_items in candidate_items { + match subpat_items { + OrderedPatternData::One(item) => flattened.push(*item), + OrderedPatternData::FromOrPattern => { + // Inline bindings/guard patterns from an or-pattern. By construction, this always // corresponds to a subcandidate and its closest descendants (i.e. those // from nested or-patterns, but not adjacent or-patterns). To handle // adjacent or-patterns, e.g. `(x | x, y | y)`, we update the `remainder` to // point to the first descendant candidate from outside this or-pattern. - if let Some(subcandidate_bindings) = remainder.next() { - push_sub_branch_bindings(flattened, subcandidate_bindings, remainder); + if let Some(subcandidate_items) = remainder.next() { + push_sub_branch_ordered_pat_data(flattened, subcandidate_items, remainder); } else { // For ill-formed patterns like `x | _`, we may not have any subcandidates left // to inline bindings from. // FIXME(@dianne): prevent ill-formed patterns from getting here ty::tls::with(|tcx| { - tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted") + tcx.dcx().delayed_bug( + "mismatched or-pattern bindings/guard patterns but no error emitted", + ) }); }; } @@ -2443,7 +2457,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { { let tcx = self.tcx; - let mut guards = sub_branch.guard_patterns; let (arm_span, arm_scope, match_scope) = self.extract_span_scope(&mut sub_branch, arm_match_scope); let guards = sub_branch.guard_patterns; diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index f1df90f93fd63..f94dacb4a37ec 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -138,7 +138,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) { for binding in &candidate.extra_data.bindings { - if let super::SubpatternBindings::One(binding) = binding { + if let super::OrderedPatternData::One(binding) = binding { self.visit_binding(binding); } } @@ -149,7 +149,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) { for binding in &flat_pat.extra_data.bindings { - if let super::SubpatternBindings::One(binding) = binding { + if let super::OrderedPatternData::One(binding) = binding { self.visit_binding(binding); } } From 1bfb34c0948eaadcc79720181d755870a949f959 Mon Sep 17 00:00:00 2001 From: human9000 Date: Wed, 8 Apr 2026 18:56:18 +0500 Subject: [PATCH 14/32] Revert "propagate `Scope` needed for guard patterns down to MIR" This reverts commit 608d56813067305c895d5ba39fbb0ee1e8170e72. --- compiler/rustc_middle/src/thir.rs | 3 --- .../src/builder/matches/match_pair.rs | 6 ----- .../src/builder/matches/mod.rs | 7 ----- .../rustc_mir_build/src/thir/pattern/mod.rs | 26 +++++-------------- compiler/rustc_mir_build/src/thir/print.rs | 2 +- 5 files changed, 7 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 8c354737ceeda..51f33691bb7dd 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -646,9 +646,6 @@ pub struct PatExtra<'tcx> { /// User-written types that must be preserved into MIR so that they can be /// checked. pub ascriptions: Vec>, - - #[type_visitable(ignore)] - pub scope: Option, } #[derive(Clone, Debug, HashStable, TypeVisitable)] diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index e3ac972c627fa..510f6d32249bb 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -365,12 +365,6 @@ impl<'tcx> MatchPairTree<'tcx> { } PatKind::Guard { ref subpattern, condition } => { - if extra_data.scope.is_none() - && let Some(ref extra) = pattern.extra - { - extra_data.scope = extra.scope - } - MatchPairTree::for_pattern(place_builder, subpattern, cx, match_pairs, extra_data); extra_data.guard_patterns.push(super::OrderedPatternData::One(condition)); return; diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index ac8198c16b5f1..66da52229d5f4 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -969,9 +969,6 @@ struct PatternExtraData<'tcx> { /// [`ExprId`]s of subpattern conditions guard_patterns: Vec>, - - /// Scope of this sub-branch - scope: Option, } impl<'tcx> PatternExtraData<'tcx> { @@ -1021,7 +1018,6 @@ impl<'tcx> FlatPat<'tcx> { ascriptions: Vec::new(), is_never: pattern.is_never_pattern(), guard_patterns: Vec::new(), - scope: None, }; MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); @@ -1436,8 +1432,6 @@ struct MatchTreeSubBranch<'tcx> { guard_patterns: Vec, /// Whether the sub-branch corresponds to a never pattern. is_never: bool, - /// Scope of this sub-branch; used mostly by guard patterns - scope: Option, } /// A branch in the output of match lowering. @@ -1495,7 +1489,6 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { &candidate.extra_data.guard_patterns, ), is_never: candidate.extra_data.is_never, - scope: candidate.extra_data.scope, } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index f84f098544658..7d4e25cd814b9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -15,10 +15,8 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, RangeEnd}; use rustc_index::Idx; -use rustc_middle::middle::region; use rustc_middle::thir::{ - Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatExtra, PatKind, PatRange, - PatRangeBoundary, + Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment}; use rustc_middle::ty::layout::IntegerExt; @@ -321,8 +319,6 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let ty = self.typeck_results.node_type(pat.hir_id); let span = pat.span; - let mut extra = None; - // Some of these match arms return a `Box` early, while others // evaluate to a `PatKind` that will become a `Box` at the end of // this function. @@ -451,27 +447,17 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, - hir::PatKind::Guard(pat, condition) => { - // In some cases during MIR lowering we need a Scope - let scope = region::Scope { - local_id: pat.hir_id.local_id, - data: region::ScopeData::MatchGuard, - }; - - extra = Some(Box::new(PatExtra { scope: Some(scope), ..Default::default() })); - - PatKind::Guard { - subpattern: self.lower_pattern(pat), - condition: self.upper.mirror_expr(condition), - } - } + hir::PatKind::Guard(pat, condition) => PatKind::Guard { + subpattern: self.lower_pattern(pat), + condition: self.upper.mirror_expr(condition), + }, hir::PatKind::Err(guar) => PatKind::Error(guar), }; // For pattern kinds that haven't already returned, create a `thir::Pat` // with the HIR pattern node's type and span. - Box::new(Pat { span, ty, kind, extra }) + Box::new(Pat { span, ty, kind, extra: None }) } fn lower_tuple_subpats( diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 387540b7133de..ea34e5f4d97df 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -695,7 +695,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { return; }; - let PatExtra { expanded_const, ascriptions, .. } = extra; + let PatExtra { expanded_const, ascriptions } = extra; print_indented!(self, "extra: PatExtra {", depth_lvl); print_indented!(self, format_args!("expanded_const: {expanded_const:?}"), depth_lvl + 1); From 49b3d5079b01802cf3f990a7087b7ae6de9b4807 Mon Sep 17 00:00:00 2001 From: human9000 Date: Fri, 10 Apr 2026 13:57:28 +0500 Subject: [PATCH 15/32] more guard pattern handling --- .../rustc_hir_analysis/src/check/region.rs | 25 ++++++++++----- compiler/rustc_middle/src/thir.rs | 6 ++++ .../src/builder/matches/mod.rs | 31 +++++++++++++++---- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index e31beca390837..9d061ea228437 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -198,13 +198,24 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: visitor.cx.var_parent = visitor.cx.parent; resolve_pat(visitor, arm.pat); - if let Some(guard) = arm.guard { - // We introduce a new scope to contain bindings and temporaries from `if let` guards, to - // ensure they're dropped before the arm's pattern's bindings. This extends to the end of - // the arm body and is the scope of its locals as well. - visitor.enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); - visitor.cx.var_parent = visitor.cx.parent; - resolve_cond(visitor, guard); + match (arm.guard, arm.pat.kind) { + (Some(arm_guard), PatKind::Guard(_, pat_guard)) => { + // We introduce a new scope to contain bindings and temporaries from `if let` guards, to + // ensure they're dropped before the arm's pattern's bindings. This extends to the end of + // the arm body and is the scope of its locals as well. + visitor + .enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); + visitor.cx.var_parent = visitor.cx.parent; + resolve_cond(visitor, arm_guard); + resolve_cond(visitor, pat_guard); + } + (Some(guard), _) | (_, PatKind::Guard(_, guard)) => { + visitor + .enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); + visitor.cx.var_parent = visitor.cx.parent; + resolve_cond(visitor, guard); + } + _ => (), } resolve_expr(visitor, arm.body, false); diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 51f33691bb7dd..9add5524fd479 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -881,6 +881,12 @@ pub enum PatKind<'tcx> { Error(ErrorGuaranteed), } +impl<'tcx> PatKind<'tcx> { + pub fn is_guard(&self) -> bool { + if let Self::Guard { .. } = self { true } else { false } + } +} + #[derive(Copy, Clone, Debug, HashStable)] pub enum DerefPatBorrowMode { Borrow(Mutability), diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 66da52229d5f4..76a0b78f4066d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -432,7 +432,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let match_scope = self.local_scope(); let guard_scope = arm .guard - .map(|_| region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }); + .map(|_| region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }) + .or_else(|| { + if let PatKind::Guard { condition, .. } = arm.pattern.kind { + Some(region::Scope { + local_id: self.thir[condition].temp_scope_id, + data: region::ScopeData::MatchGuard, + }) + } else { + None + } + }); self.in_scope(arm_scope, LintLevel::Explicit(arm.hir_id), |this| { this.opt_in_scope(guard_scope.map(|scope| (scope, arm_source_info)), |this| { // `if let` guard temps needing deduplicating will be in the guard scope. @@ -731,7 +741,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { var, ty, user_tys, - ArmHasGuard(guard.is_some()), + ArmHasGuard(guard.is_some() || pattern.kind.is_guard()), opt_match_place.map(|(x, y)| (x.cloned(), y)), pattern.span, ); @@ -741,6 +751,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(guard_expr) = guard { self.declare_guard_bindings(guard_expr, scope_span, visibility_scope); } + if let PatKind::Guard { condition, ..} = pattern.kind { + self.declare_guard_bindings(condition, scope_span, visibility_scope); + }; visibility_scope } @@ -2457,7 +2470,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. // This always schedules storage drops, so we may need to unschedule them below. - self.bind_matched_candidate_for_guard(block, sub_branch.bindings.iter()); + let _ = self.in_scope( + (match_scope, self.source_info(arm_span)), + LintLevel::Inherited, + |this| { + this.bind_matched_candidate_for_guard(block, sub_branch.bindings.iter()); + BasicBlock::ZERO.unit() + }, + ); let guard_frame = GuardFrame { locals: sub_branch .bindings @@ -2477,7 +2497,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let (post_guard_block, otherwise_post_guard_block) = self.in_if_then_scope(match_scope, arm_span, |this| { - guards.into_iter().fold(BlockAnd(block, ()), |block, guard| { + guards.into_iter().fold(block.unit(), |block, guard| { this.then_else_break( block.0, guard, @@ -2580,8 +2600,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { let span = sub_branch.span; // There must be a scope if a guard pattern is present - let scope = sub_branch.scope.unwrap(); - + let scope = todo!(); (span, scope, scope) }; (arm_span, arm_scope, match_scope) From 4675c33e9fb550606b43978c72f9dcd8897ce8a0 Mon Sep 17 00:00:00 2001 From: human9000 Date: Fri, 10 Apr 2026 14:12:55 +0500 Subject: [PATCH 16/32] decouple `arm_match_scope` + rustfmt If `arm` is not provided, we still need `match_scope` in order to properly lower guard patterns --- .../src/builder/matches/mod.rs | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 76a0b78f4066d..2e70a89c498b8 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -476,7 +476,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { branch, &built_match_tree.fake_borrow_temps, scrutinee_span, - Some((arm, match_scope)), + Some(arm), + match_scope, ); this.fixed_temps_scope = old_dedup_scope; @@ -530,7 +531,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { branch: MatchTreeBranch<'tcx>, fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, - arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, + arm: Option<&Arm<'tcx>>, + match_scope: region::Scope, ) -> BasicBlock { if branch.sub_branches.len() == 1 { let [sub_branch] = branch.sub_branches.try_into().unwrap(); @@ -539,7 +541,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sub_branch, fake_borrow_temps, scrutinee_span, - arm_match_scope, + arm, + match_scope, ScheduleDrops::Yes, ) } else { @@ -568,7 +571,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sub_branch, fake_borrow_temps, scrutinee_span, - arm_match_scope, + arm, + match_scope, schedule_drops, ); self.cfg.goto(binding_end, outer_source_info, target_block); @@ -699,12 +703,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + let match_scope = self.local_scope(); self.bind_pattern( self.source_info(irrefutable_pat.span), branch, &[], irrefutable_pat.span, None, + match_scope, ) .unit() } @@ -751,7 +757,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(guard_expr) = guard { self.declare_guard_bindings(guard_expr, scope_span, visibility_scope); } - if let PatKind::Guard { condition, ..} = pattern.kind { + if let PatKind::Guard { condition, .. } = pattern.kind { self.declare_guard_bindings(condition, scope_span, visibility_scope); }; visibility_scope @@ -2420,7 +2426,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - let success = self.bind_pattern(self.source_info(pat.span), branch, &[], expr_span, None); + let match_scope = self.local_scope(); + let success = self.bind_pattern( + self.source_info(pat.span), + branch, + &[], + expr_span, + None, + match_scope, + ); // If branch coverage is enabled, record this branch. self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block); @@ -2441,7 +2455,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mut sub_branch: MatchTreeSubBranch<'tcx>, fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, - arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, + arm: Option<&Arm<'tcx>>, + match_scope: region::Scope, schedule_drops: ScheduleDrops, ) -> BasicBlock { debug!("bind_and_guard_matched_candidate(subbranch={:?})", sub_branch); @@ -2458,13 +2473,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.ascribe_types(block, mem::take(&mut sub_branch.ascriptions)); - if !sub_branch.guard_patterns.is_empty() - || arm_match_scope.is_some_and(|(arm, _)| arm.guard.is_some()) - { + if !sub_branch.guard_patterns.is_empty() || arm.is_some_and(|arm| arm.guard.is_some()) { let tcx = self.tcx; - let (arm_span, arm_scope, match_scope) = - self.extract_span_scope(&mut sub_branch, arm_match_scope); + let (arm_span, arm_scope) = self.extract_arm_span_scope(&mut sub_branch, arm); let guards = sub_branch.guard_patterns; // Bindings for guards require some extra handling to automatically @@ -2585,25 +2597,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - fn extract_span_scope( + fn extract_arm_span_scope( &mut self, sub_branch: &mut MatchTreeSubBranch<'tcx>, - arm_match_scope: Option<(&Arm<'tcx>, Scope)>, - ) -> (Span, Scope, Scope) { - let (arm_span, arm_scope, match_scope) = if let Some((arm, match_scope)) = arm_match_scope { + arm: Option<&Arm<'tcx>>, + ) -> (Span, Scope) { + if let Some(arm) = arm { let mut span = arm.span; if let Some(arm_guard) = arm.guard { span = span.to(self.thir[arm_guard].span); sub_branch.guard_patterns.push(arm_guard); }; - (span, arm.scope, match_scope) + return (span, arm.scope); } else { let span = sub_branch.span; - // There must be a scope if a guard pattern is present - let scope = todo!(); - (span, scope, scope) + let arm_scope = self.local_scope(); + return (span, arm_scope); }; - (arm_span, arm_scope, match_scope) } /// Append `AscribeUserType` statements onto the end of `block` From 2235647d455d3b57c7aab844686b060cceec8609 Mon Sep 17 00:00:00 2001 From: human9000 Date: Fri, 10 Apr 2026 20:18:41 +0500 Subject: [PATCH 17/32] Propagate the fact of guard pattern's presense when visiting it's subpatterns --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 2e70a89c498b8..4b68fd3f22159 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -731,7 +731,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings_special( pattern, &ProjectedUserTypesNode::None, - &mut |this, name, mode, var, span, ty, user_tys| { + false, + &mut |this, name, mode, var, span, ty, user_tys, has_guard_pattern| { let saved_scope = this.source_scope; this.set_correct_source_scope_for_arg(var.0, saved_scope, span); let vis_scope = *visibility_scope @@ -747,7 +748,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { var, ty, user_tys, - ArmHasGuard(guard.is_some() || pattern.kind.is_guard()), + ArmHasGuard(guard.is_some() || has_guard_pattern), opt_match_place.map(|(x, y)| (x.cloned(), y)), pattern.span, ); @@ -860,6 +861,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, pattern: &Pat<'tcx>, user_tys: &ProjectedUserTypesNode<'_>, + mut guard_pat_present: bool, f: &mut impl FnMut( &mut Self, Symbol, @@ -868,8 +870,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Span, Ty<'tcx>, &ProjectedUserTypesNode<'_>, + bool, ), ) { + guard_pat_present |= pattern.kind.is_guard(); + // Ascriptions correspond to user-written types like `let A::<'a>(_): A<'static> = ...;`. // // Caution: Pushing user types here is load-bearing even for @@ -892,13 +897,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Avoid having to write the full method name at each recursive call. let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| { - this.visit_primary_bindings_special(subpat, user_tys, f) + this.visit_primary_bindings_special(subpat, user_tys, guard_pat_present, f) }; match pattern.kind { PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => { if is_primary { - f(self, name, mode, var, pattern.span, ty, user_tys); + f(self, name, mode, var, pattern.span, ty, user_tys, guard_pat_present); } if let Some(subpattern) = subpattern.as_ref() { visit_subpat(self, subpattern, user_tys, f); From ff38c3a9af483030d2bfd67557c84ddd9b3fb573 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 11 Apr 2026 14:27:20 +0500 Subject: [PATCH 18/32] Proper spanning for arms and guards --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 4b68fd3f22159..43e175b8b7a94 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2610,7 +2610,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(arm) = arm { let mut span = arm.span; if let Some(arm_guard) = arm.guard { - span = span.to(self.thir[arm_guard].span); + span = self.thir[arm_guard].span; sub_branch.guard_patterns.push(arm_guard); }; return (span, arm.scope); From a37a3f913443a55515991a7f0de8a4607a138801 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 11 Apr 2026 15:29:47 +0500 Subject: [PATCH 19/32] Update docs related to `sub_branch_ordered_pat_data` --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 43e175b8b7a94..2b699218e0d12 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1571,7 +1571,7 @@ fn sub_branch_ordered_pat_data<'a, T: Copy>( all_items } -/// Helper for [`sub_branch_ordered_data`]. Collects bindings/guard patterns from `candidate_items` into +/// Helper for [`sub_branch_ordered_pat_data`]. Collects bindings/guard patterns from `candidate_items` into /// `flattened`. Those items in or-patterns are collected recursively from `remainder`. fn push_sub_branch_ordered_pat_data<'c, T: Copy>( flattened: &mut Vec, From b4cc11aaf74afbc5a293828dae6b1f80103fdb12 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 11 Apr 2026 15:53:53 +0500 Subject: [PATCH 20/32] Leave `if let` guard patterns handling alone (for now) --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 2b699218e0d12..0e10bd0e8b5b3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -755,12 +755,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.source_scope = saved_scope; }, ); + // FIXME(guard_patterns): same kind of handling will be need to support `if let` guards. if let Some(guard_expr) = guard { self.declare_guard_bindings(guard_expr, scope_span, visibility_scope); } - if let PatKind::Guard { condition, .. } = pattern.kind { - self.declare_guard_bindings(condition, scope_span, visibility_scope); - }; visibility_scope } From e756562b902b797aa1e96b5d08c69158d93a4eb3 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 11 Apr 2026 18:42:54 +0500 Subject: [PATCH 21/32] Check if guards are present in a pattern in separate pass Co-authored-by: dianne --- .../src/builder/matches/mod.rs | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 0e10bd0e8b5b3..0ef5cd5107337 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -728,11 +728,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { + let has_guard = guard.is_some() || self.pat_has_guard(pattern); self.visit_primary_bindings_special( pattern, &ProjectedUserTypesNode::None, - false, - &mut |this, name, mode, var, span, ty, user_tys, has_guard_pattern| { + &mut |this, name, mode, var, span, ty, user_tys| { let saved_scope = this.source_scope; this.set_correct_source_scope_for_arg(var.0, saved_scope, span); let vis_scope = *visibility_scope @@ -748,7 +748,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { var, ty, user_tys, - ArmHasGuard(guard.is_some() || has_guard_pattern), + ArmHasGuard(has_guard), opt_match_place.map(|(x, y)| (x.cloned(), y)), pattern.span, ); @@ -790,6 +790,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + fn pat_has_guard(&self, pat: &Pat<'tcx>) -> bool { + match &pat.kind { + PatKind::Guard { .. } => return true, + PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { + return prefix.iter().any(|subpat| self.pat_has_guard(subpat)) + || slice.as_deref().is_some_and(|subpat| self.pat_has_guard(subpat)) + || suffix.iter().any(|subpat| self.pat_has_guard(subpat)); + } + PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => { + subpatterns.iter().any(|subpat| self.pat_has_guard(&subpat.pattern)) + } + PatKind::Or { pats: subpatterns } => { + subpatterns.iter().any(|subpat| self.pat_has_guard(subpat)) + } + PatKind::Binding { subpattern, .. } => { + return subpattern.as_deref().is_some_and(|subpat| self.pat_has_guard(subpat)); + } + PatKind::Deref { subpattern, .. } | PatKind::DerefPattern { subpattern, .. } => { + return self.pat_has_guard(subpattern.as_ref()); + } + _ => false, + } + } + /// Emits a [`StatementKind::StorageLive`] for the given var, and also /// schedules a drop if requested (and possible). pub(crate) fn storage_live_binding( @@ -859,7 +883,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, pattern: &Pat<'tcx>, user_tys: &ProjectedUserTypesNode<'_>, - mut guard_pat_present: bool, f: &mut impl FnMut( &mut Self, Symbol, @@ -868,11 +891,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Span, Ty<'tcx>, &ProjectedUserTypesNode<'_>, - bool, ), ) { - guard_pat_present |= pattern.kind.is_guard(); - // Ascriptions correspond to user-written types like `let A::<'a>(_): A<'static> = ...;`. // // Caution: Pushing user types here is load-bearing even for @@ -895,13 +915,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Avoid having to write the full method name at each recursive call. let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| { - this.visit_primary_bindings_special(subpat, user_tys, guard_pat_present, f) + this.visit_primary_bindings_special(subpat, user_tys, f) }; match pattern.kind { PatKind::Binding { name, mode, var, ty, ref subpattern, is_primary, .. } => { if is_primary { - f(self, name, mode, var, pattern.span, ty, user_tys, guard_pat_present); + f(self, name, mode, var, pattern.span, ty, user_tys); } if let Some(subpattern) = subpattern.as_ref() { visit_subpat(self, subpattern, user_tys, f); From a3b78154239c238382c23d460c06c6a264b6d69e Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 11 Apr 2026 19:36:49 +0500 Subject: [PATCH 22/32] Don't enter the same scope twice --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 0ef5cd5107337..db18e2ff691dd 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2505,14 +2505,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. // This always schedules storage drops, so we may need to unschedule them below. - let _ = self.in_scope( - (match_scope, self.source_info(arm_span)), - LintLevel::Inherited, - |this| { - this.bind_matched_candidate_for_guard(block, sub_branch.bindings.iter()); - BasicBlock::ZERO.unit() - }, - ); + self.bind_matched_candidate_for_guard(block, sub_branch.bindings.iter()); let guard_frame = GuardFrame { locals: sub_branch .bindings From dbe705d9b02a2d1fa009092c671a871111064797 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 11 Apr 2026 21:53:14 +0500 Subject: [PATCH 23/32] Properly handle guard patterns in `ScopeResolutionVisitor` Co-authored-by: dianne --- .../rustc_hir_analysis/src/check/region.rs | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 9d061ea228437..1dc655f372313 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -198,24 +198,13 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: visitor.cx.var_parent = visitor.cx.parent; resolve_pat(visitor, arm.pat); - match (arm.guard, arm.pat.kind) { - (Some(arm_guard), PatKind::Guard(_, pat_guard)) => { - // We introduce a new scope to contain bindings and temporaries from `if let` guards, to - // ensure they're dropped before the arm's pattern's bindings. This extends to the end of - // the arm body and is the scope of its locals as well. - visitor - .enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); - visitor.cx.var_parent = visitor.cx.parent; - resolve_cond(visitor, arm_guard); - resolve_cond(visitor, pat_guard); - } - (Some(guard), _) | (_, PatKind::Guard(_, guard)) => { - visitor - .enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); - visitor.cx.var_parent = visitor.cx.parent; - resolve_cond(visitor, guard); - } - _ => (), + if let Some(guard) = arm.guard { + // We introduce a new scope to contain bindings and temporaries from `if let` guards, to + // ensure they're dropped before the arm's pattern's bindings. This extends to the end of + // the arm body and is the scope of its locals as well. + visitor.enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); + visitor.cx.var_parent = visitor.cx.parent; + resolve_cond(visitor, guard); } resolve_expr(visitor, arm.body, false); @@ -224,6 +213,13 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: #[tracing::instrument(level = "debug", skip(visitor))] fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + // We walk the whole pattern here to avoid walking `cond` expr another time on `walk_pat` + if let PatKind::Guard(pat, cond) = pat.kind { + resolve_cond(visitor, cond); + resolve_pat(visitor, pat); + return; + } + // If this is a binding then record the lifetime of that binding. if let PatKind::Binding(..) = pat.kind { record_var_lifetime(visitor, pat.hir_id.local_id); From af46f9556cef00e0bad44031c3073310a7dff67b Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 12 Apr 2026 20:14:46 +0500 Subject: [PATCH 24/32] Properly account guard patterns when entering guard scope --- .../rustc_mir_build/src/builder/matches/mod.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index db18e2ff691dd..a2f6ab5ca2ab6 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -430,19 +430,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_source_info = self.source_info(arm.span); let arm_scope = (arm.scope, arm_source_info); let match_scope = self.local_scope(); - let guard_scope = arm - .guard - .map(|_| region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }) - .or_else(|| { - if let PatKind::Guard { condition, .. } = arm.pattern.kind { - Some(region::Scope { - local_id: self.thir[condition].temp_scope_id, - data: region::ScopeData::MatchGuard, - }) - } else { - None - } - }); + let guard_scope = if arm.guard.is_some() || self.pat_has_guard(&arm.pattern) { + Some(region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }) + } else { + None + }; self.in_scope(arm_scope, LintLevel::Explicit(arm.hir_id), |this| { this.opt_in_scope(guard_scope.map(|scope| (scope, arm_source_info)), |this| { // `if let` guard temps needing deduplicating will be in the guard scope. From 9db9f152feeda154a267e8746187e8aa229f83c2 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 18 Apr 2026 22:16:45 +0500 Subject: [PATCH 25/32] remove `is_guard` method --- compiler/rustc_middle/src/thir.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 9add5524fd479..51f33691bb7dd 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -881,12 +881,6 @@ pub enum PatKind<'tcx> { Error(ErrorGuaranteed), } -impl<'tcx> PatKind<'tcx> { - pub fn is_guard(&self) -> bool { - if let Self::Guard { .. } = self { true } else { false } - } -} - #[derive(Copy, Clone, Debug, HashStable)] pub enum DerefPatBorrowMode { Borrow(Mutability), From 00f0ffe15d19c8c76a34edd5a751d257aa2df180 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 18 Apr 2026 22:35:55 +0500 Subject: [PATCH 26/32] Simplify `pat_has_guard` --- .../src/builder/matches/mod.rs | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index a2f6ab5ca2ab6..a881a28c8d828 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -783,27 +783,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } fn pat_has_guard(&self, pat: &Pat<'tcx>) -> bool { - match &pat.kind { - PatKind::Guard { .. } => return true, - PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { - return prefix.iter().any(|subpat| self.pat_has_guard(subpat)) - || slice.as_deref().is_some_and(|subpat| self.pat_has_guard(subpat)) - || suffix.iter().any(|subpat| self.pat_has_guard(subpat)); - } - PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => { - subpatterns.iter().any(|subpat| self.pat_has_guard(&subpat.pattern)) - } - PatKind::Or { pats: subpatterns } => { - subpatterns.iter().any(|subpat| self.pat_has_guard(subpat)) - } - PatKind::Binding { subpattern, .. } => { - return subpattern.as_deref().is_some_and(|subpat| self.pat_has_guard(subpat)); - } - PatKind::Deref { subpattern, .. } | PatKind::DerefPattern { subpattern, .. } => { - return self.pat_has_guard(subpattern.as_ref()); - } - _ => false, - } + let mut has_guard = false; + pat.walk(|pat| { + if matches!(pat.kind, PatKind::Guard { .. }) { + has_guard = true; + }; + // If has_guard is true, we just stop walking other patterns + !has_guard + }); + has_guard } /// Emits a [`StatementKind::StorageLive`] for the given var, and also From 0282eeb03bfb365c08c3b92dcd381a5d99614d33 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sat, 18 Apr 2026 22:36:22 +0500 Subject: [PATCH 27/32] fixmes --- compiler/rustc_mir_build/src/builder/matches/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index a881a28c8d828..165a819c223f6 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -695,6 +695,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + // FIXME(guard_patterns) let match_scope = self.local_scope(); self.bind_pattern( self.source_info(irrefutable_pat.span), @@ -2429,6 +2430,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + // FIXME(guard_patterns) let match_scope = self.local_scope(); let success = self.bind_pattern( self.source_info(pat.span), From d036e0bade0cf6cc4bdc1d9e49a3cd89a8f9974f Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 19 Apr 2026 13:51:25 +0500 Subject: [PATCH 28/32] refactor `extract_span_scope` --- .../src/builder/matches/mod.rs | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 165a819c223f6..cb58b67686118 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2481,8 +2481,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if !sub_branch.guard_patterns.is_empty() || arm.is_some_and(|arm| arm.guard.is_some()) { let tcx = self.tcx; - let (arm_span, arm_scope) = self.extract_arm_span_scope(&mut sub_branch, arm); - let guards = sub_branch.guard_patterns; + let (arm_span, arm_scope) = self.extract_arm_span_scope(sub_branch.span, arm); // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. @@ -2596,22 +2595,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } fn extract_arm_span_scope( - &mut self, - sub_branch: &mut MatchTreeSubBranch<'tcx>, + &self, + sub_branch_span: Span, arm: Option<&Arm<'tcx>>, ) -> (Span, Scope) { - if let Some(arm) = arm { - let mut span = arm.span; - if let Some(arm_guard) = arm.guard { - span = self.thir[arm_guard].span; - sub_branch.guard_patterns.push(arm_guard); - }; - return (span, arm.scope); - } else { - let span = sub_branch.span; - let arm_scope = self.local_scope(); - return (span, arm_scope); - }; + arm.map(|arm| { + let span = arm.guard.map(|guard| self.thir[guard].span).unwrap_or(arm.span); + (span, arm.scope) + }) + // FIXME(guard_patterns) + .unwrap_or_else(|| (sub_branch_span, self.local_scope())) } /// Append `AscribeUserType` statements onto the end of `block` From a32bc5b55108d960f2b6b9f2302b2cb77b536603 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 19 Apr 2026 14:03:02 +0500 Subject: [PATCH 29/32] Merge arm/pat guards in `Candidate` creation. Also tweak wording around arm/guard patterns in docs and comments Co-authored-by: dianne --- .../src/builder/matches/mod.rs | 60 +++++++++++-------- .../src/builder/matches/util.rs | 2 +- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index cb58b67686118..80e65755049ee 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -365,8 +365,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .iter() .map(|&arm| { let arm = &self.thir[arm]; - let has_match_guard = - if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No }; + let has_match_guard = if let Some(guard) = arm.guard { + HasMatchGuard::Yes(guard) + } else { + HasMatchGuard::No + }; (&*arm.pattern, has_match_guard) }) .collect(); @@ -504,7 +507,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// For a top-level `match` arm or a `let` binding, binds the variables and - /// ascribes types, and also checks the match arm guard (if present). + /// ascribes types, and also checks the match arm/pattern guards (if present). /// /// `arm_scope` should be `Some` if and only if this is called for a /// `match` arm. @@ -1099,12 +1102,11 @@ struct Candidate<'tcx> { /// - See [`Builder::remove_never_subcandidates`]. subcandidates: Vec>, - /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`. + /// ...and if there are guards they must be evaluated; if they're `false` then branch to `otherwise_block`. /// /// --- - /// For subcandidates, this is copied from the parent candidate, so it indicates - /// whether the enclosing match arm has a guard. - has_guard: bool, + /// For subcandidates, this is copied from the parent candidate + guards: Vec>, /// Holds extra pattern data that was prepared by [`FlatPat`], including bindings and /// ascriptions that must be established if this candidate succeeds. @@ -1136,23 +1138,25 @@ impl<'tcx> Candidate<'tcx> { fn new( place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, - has_guard: HasMatchGuard, + guard: HasMatchGuard, cx: &mut Builder<'_, 'tcx>, ) -> Self { // Use `FlatPat` to build simplified match pairs, then immediately // incorporate them into a new candidate. let flat_pat = FlatPat::new(place, pattern, cx); - let has_guard = matches!(has_guard, HasMatchGuard::Yes) - || !flat_pat.extra_data.guard_patterns.is_empty(); - Self::from_flat_pat(flat_pat, has_guard) + let mut guards = flat_pat.extra_data.guard_patterns.clone(); + if let HasMatchGuard::Yes(g) = guard { + guards.push(OrderedPatternData::One(g)); + }; + Self::from_flat_pat(flat_pat, guards) } /// Incorporates an already-simplified [`FlatPat`] into a new candidate. - fn from_flat_pat(flat_pat: FlatPat<'tcx>, has_guard: bool) -> Self { + fn from_flat_pat(flat_pat: FlatPat<'tcx>, guards: Vec>) -> Self { let mut this = Candidate { match_pairs: flat_pat.match_pairs, extra_data: flat_pat.extra_data, - has_guard, + guards, subcandidates: Vec::new(), or_span: None, otherwise_block: None, @@ -1451,8 +1455,8 @@ struct MatchTreeSubBranch<'tcx> { bindings: Vec>, /// The ascriptions to set up in this sub-branch. ascriptions: Vec>, - /// The guard patterns present in this sub-branch - guard_patterns: Vec, + /// The guards from arms and patterns combined present in this sub-branch. + guards: Vec, /// Whether the sub-branch corresponds to a never pattern. is_never: bool, } @@ -1507,9 +1511,9 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { .cloned() .chain(candidate.extra_data.ascriptions) .collect(), - guard_patterns: sub_branch_ordered_pat_data( + guards: sub_branch_ordered_pat_data( parent_data.iter().map(|parent| parent.guard_patterns.as_slice()), - &candidate.extra_data.guard_patterns, + &candidate.guards, ), is_never: candidate.extra_data.is_never, } @@ -1605,7 +1609,7 @@ fn push_sub_branch_ordered_pat_data<'c, T: Copy>( #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum HasMatchGuard { - Yes, + Yes(ExprId), No, } @@ -1659,7 +1663,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // false edge to the final failure block. let mut next_candidate_start_block = if refutable { Some(otherwise_block) } else { None }; for candidate in candidates.iter_mut().rev() { - let has_guard = candidate.has_guard; + let has_guard = !candidate.guards.is_empty(); candidate.visit_leaves_rev(|leaf_candidate| { if let Some(next_candidate_start_block) = next_candidate_start_block { let source_info = self.source_info(leaf_candidate.extra_data.span); @@ -2006,7 +2010,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.or_span = Some(match_pair.pattern_span); candidate.subcandidates = pats .into_iter() - .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) + .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.guards.clone())) .collect(); candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; } @@ -2070,7 +2074,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// in match tree lowering. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'tcx>) { assert!(!candidate.subcandidates.is_empty()); - if candidate.has_guard { + if !candidate.guards.is_empty() { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } @@ -2190,7 +2194,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // directly to `last_otherwise`. If there is a guard, // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we // can't skip `Q`. - let or_otherwise = if leaf_candidate.has_guard { + let or_otherwise = if !leaf_candidate.guards.is_empty() { leaf_candidate.otherwise_block.unwrap() } else { last_otherwise.unwrap() @@ -2448,7 +2452,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Initializes each of the bindings from the candidate by - /// moving/copying/ref'ing the source as appropriate. Tests the guard, if + /// moving/copying/ref'ing the source as appropriate. Tests the guards, if /// any, and then branches to the arm. Returns the block for the case where /// the guard succeeds. /// @@ -2457,7 +2461,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// moving the binding once the guard has evaluated to true (see below). fn bind_and_guard_matched_candidate( &mut self, - mut sub_branch: MatchTreeSubBranch<'tcx>, + sub_branch: MatchTreeSubBranch<'tcx>, fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, arm: Option<&Arm<'tcx>>, @@ -2476,9 +2480,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return self.cfg.start_new_block(); } - self.ascribe_types(block, mem::take(&mut sub_branch.ascriptions)); + self.ascribe_types(block, sub_branch.ascriptions); + + let guards = sub_branch.guards; - if !sub_branch.guard_patterns.is_empty() || arm.is_some_and(|arm| arm.guard.is_some()) { + // Lower guards of this sub-branch (if present) for this candidate, + // and then perform bindings for the arm body. + if !guards.is_empty() { let tcx = self.tcx; let (arm_span, arm_scope) = self.extract_arm_span_scope(sub_branch.span, arm); diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index f94dacb4a37ec..1dc7cc7ca1810 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -72,7 +72,7 @@ pub(super) fn collect_fake_borrows<'tcx>( temp_span: Span, scrutinee_base: PlaceBase, ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { - if candidates.iter().all(|candidate| !candidate.has_guard) { + if candidates.iter().all(|candidate| candidate.guards.is_empty()) { // Fake borrows are only used when there is a guard. return Vec::new(); } From 4d886218a76aff64db3890310868d979a2ebfedb Mon Sep 17 00:00:00 2001 From: human9000 Date: Mon, 20 Apr 2026 14:03:01 +0500 Subject: [PATCH 30/32] Update match_pair.rs --- compiler/rustc_mir_build/src/builder/matches/match_pair.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 510f6d32249bb..a57257608dda5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -138,6 +138,8 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, PatKind::Or { ref pats } => { + use super::OrderedPatternData; + let pats: Box<[FlatPat<'tcx>]> = pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(); if !pats[0].extra_data.bindings.is_empty() { @@ -146,9 +148,9 @@ impl<'tcx> MatchPairTree<'tcx> { // or-patterns that will be simplified by `merge_trivial_subcandidates`. In // other words, we can assume this expands into subcandidates. // FIXME(@dianne): this needs updating/removing if we always merge or-patterns - extra_data.bindings.push(super::OrderedPatternData::FromOrPattern); + extra_data.bindings.push(OrderedPatternData::FromOrPattern); } - if !pats[0].extra_data.guard_patterns.is_empty() { + if pats.iter().any(|pat| !pat.extra_data.guard_patterns.is_empty()) { extra_data.guard_patterns.push(super::OrderedPatternData::FromOrPattern); } Some(TestableCase::Or { pats }) From 4188f21f500eed7ff131cfade887484249e59b0a Mon Sep 17 00:00:00 2001 From: human9000 Date: Mon, 20 Apr 2026 14:20:47 +0500 Subject: [PATCH 31/32] add runtime behavior testing for guard patterns (1) --- .../rfc-3637-guard-patterns/correct-mir.rs | 20 -------- .../runtime-behavior/arm-and-pat-guards.rs | 22 +++++++++ .../runtime-behavior/or-pats.rs | 48 +++++++++++++++++++ .../runtime-behavior/with-bools.rs | 34 +++++++++++++ 4 files changed, 104 insertions(+), 20 deletions(-) delete mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/arm-and-pat-guards.rs create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/or-pats.rs create mode 100644 tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/with-bools.rs diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs b/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs deleted file mode 100644 index cf7f824eb0d24..0000000000000 --- a/tests/ui/pattern/rfc-3637-guard-patterns/correct-mir.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Check MIR correctness for general guard patterns usage - -//@ compile-flags: -Zvalidate-mir -Zlint-mir -//@ run-pass - -#![feature(guard_patterns)] -#![allow(incomplete_features)] - -fn main() { - assert!(generic_usage(true, false, true)); -} - -fn generic_usage(x: bool, y: bool, z: bool) -> bool { - match (x, y) { - (true if z, false if z) => true, - (false if z, true if z) => false, - (true, true) => true, - (false, false) => false - } -} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/arm-and-pat-guards.rs b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/arm-and-pat-guards.rs new file mode 100644 index 0000000000000..22c00cae2a56f --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/arm-and-pat-guards.rs @@ -0,0 +1,22 @@ +//! Check for correct runtime behavior when using guard patterns combined with arm guards + +//@ compile-flags: -Zvalidate-mir -Zlint-mir +//@ run-pass + +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +fn main() { + assert!(guard_arm_pats(true, false, true)); + assert!(guard_arm_pats(false, true, true)); +} + +fn guard_arm_pats(x: bool, y: bool, z: bool) -> bool { + match (x, y) { + // (true, false, true) + (true if x, false if x) if z => true, + // (false, true, true) + (false if z, true if !x) if y => true, + _ => false + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/or-pats.rs b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/or-pats.rs new file mode 100644 index 0000000000000..edaa55d4b2832 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/or-pats.rs @@ -0,0 +1,48 @@ +//! Check for correct runtime behavior when using guard patterns with or-patterns + +//@ compile-flags: -Zvalidate-mir -Zlint-mir +//@ run-pass + +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +fn main() { + assert!(arr_with_or_pats_and_arm_guard([1, 2, 4], true, true, false, true, true)); + assert!(arr_with_or_pats_and_arm_guard([1, 3, 4], true, false, true, true, true)); + + assert!(arr_with_or_pats([1, 2, 4], true, true, false, true)); + assert!(arr_with_or_pats([1, 3, 4], true, false, true, true)); +} + +fn arr_with_or_pats_and_arm_guard( + arr: [u8; 3], + a: bool, + b: bool, + c: bool, + d: bool, + e: bool, +) -> bool { + const A: u8 = 1; + const B: u8 = 2; + const C: u8 = 3; + const D: u8 = 4; + + match arr { + // [1, 2 | 3, 4] + [A if a, (B if b) | (C if c), D if d] if e => true, + _ => false + } +} + +fn arr_with_or_pats(arr: [u8; 3], a: bool, b: bool, c: bool, d: bool) -> bool { + const A: u8 = 1; + const B: u8 = 2; + const C: u8 = 3; + const D: u8 = 4; + + match arr { + // [1, 2 | 3, 4] + [A if a, (B if b) | (C if c), D if d] => true, + _ => false + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/with-bools.rs b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/with-bools.rs new file mode 100644 index 0000000000000..44e58fad38510 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/with-bools.rs @@ -0,0 +1,34 @@ +//! Check for correct runtime behavior when using bools in guard patterns + +//@ compile-flags: -Zvalidate-mir -Zlint-mir +//@ run-pass + +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +fn main() { + assert!(generic_usage(true, false, true)); + assert!(!generic_usage(false, true, true)); + assert!(with_ops(true, false, true)); + assert!(with_ops(false, true, true)) +} + +fn generic_usage(x: bool, y: bool, z: bool) -> bool { + match (x, y) { + // (true, false, true) + (true if z, false if z) => true, + // (false, true, true) + (false if z, true if z) => false, + _ => false + } +} + +fn with_ops(x: bool, y: bool, z: bool) -> bool { + match (x, y) { + // (true, false, true) + (true if y || z, false if x && z) => true, + // (false, true, true) + (false if y && z, true if y || z) => true, + _ => false + } + } From cef0cf136feccfe73c30f6a0d486bb3559aa6871 Mon Sep 17 00:00:00 2001 From: human9000 Date: Fri, 1 May 2026 16:27:39 +0500 Subject: [PATCH 32/32] Update arm + guard pat combination and guard pat handling overall --- .../src/builder/matches/mod.rs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 80e65755049ee..02882fa831bee 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -999,7 +999,7 @@ struct PatternExtraData<'tcx> { impl<'tcx> PatternExtraData<'tcx> { fn is_empty(&self) -> bool { - self.bindings.is_empty() && self.ascriptions.is_empty() + self.bindings.is_empty() && self.ascriptions.is_empty() && self.guard_patterns.is_empty() } } @@ -1102,11 +1102,12 @@ struct Candidate<'tcx> { /// - See [`Builder::remove_never_subcandidates`]. subcandidates: Vec>, - /// ...and if there are guards they must be evaluated; if they're `false` then branch to `otherwise_block`. + /// ...and if there are arm guards they must be evaluated; if they're `false` then branch to + /// `otherwise_block`. /// /// --- /// For subcandidates, this is copied from the parent candidate - guards: Vec>, + guards: Vec, /// Holds extra pattern data that was prepared by [`FlatPat`], including bindings and /// ascriptions that must be established if this candidate succeeds. @@ -1144,15 +1145,15 @@ impl<'tcx> Candidate<'tcx> { // Use `FlatPat` to build simplified match pairs, then immediately // incorporate them into a new candidate. let flat_pat = FlatPat::new(place, pattern, cx); - let mut guards = flat_pat.extra_data.guard_patterns.clone(); + let mut guards = Vec::new(); if let HasMatchGuard::Yes(g) = guard { - guards.push(OrderedPatternData::One(g)); - }; + guards.push(g); + } Self::from_flat_pat(flat_pat, guards) } /// Incorporates an already-simplified [`FlatPat`] into a new candidate. - fn from_flat_pat(flat_pat: FlatPat<'tcx>, guards: Vec>) -> Self { + fn from_flat_pat(flat_pat: FlatPat<'tcx>, guards: Vec) -> Self { let mut this = Candidate { match_pairs: flat_pat.match_pairs, extra_data: flat_pat.extra_data, @@ -1513,8 +1514,11 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { .collect(), guards: sub_branch_ordered_pat_data( parent_data.iter().map(|parent| parent.guard_patterns.as_slice()), - &candidate.guards, - ), + &candidate.extra_data.guard_patterns, + ) + .into_iter() + .chain(candidate.guards) + .collect(), is_never: candidate.extra_data.is_never, } } @@ -2074,7 +2078,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// in match tree lowering. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'tcx>) { assert!(!candidate.subcandidates.is_empty()); - if !candidate.guards.is_empty() { + if !candidate.guards.is_empty() || !candidate.extra_data.guard_patterns.is_empty() { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; }